从GCD dispatch_async块中调用super:是否安全?

时间:2021-03-19 05:18:56

I'm in a bit of a pickle. I know that calling [self methodName] from within a block will lead to a retain cycle.

我有点麻烦。我知道从一个块中调用[self methodName]将会导致一个retain cycle。

However in this class due to multithreading I cannot allow execution of the method that the block is accessing from anywhere else other than the block, as it would potentially lead to serious problems.

但是在这个类中,由于多线程,我不能允许执行块从其他任何地方访问的方法,因为它可能会导致严重的问题。

Current code:

当前代码:

 if (_getRunning==NO){
        __weak SyncArrayMT *_weak_self = self;
        _get_t = ^void (void){
            _weak_self->_getRunning = YES;
            NSArray *objects = [_weak_self get:getQuery 
                                usingClassCtor:ctor 
                                 withAuthBlock:authBlock];
            if (_weak_self.getBlockCb)
                _weak_self.getBlockCb(objects);
            _weak_self->_getRunning = NO;
        };
    }

Does exactly that, it calls [self getmethod]. While its ok for the dispatched block to run this method, I do not want anything outside this class calling this method. So, would it be ok to override this inherited method as such:

它会调用[self getmethod]。虽然调度块可以运行这个方法,但是我不想在这个类之外调用这个方法。因此,是否可以重写这个继承的方法,如:

- (NSArray *) get:(NSString *)getQuery usingClassCtor:(initBlock)initCb withAuthBlock:(authenticate)authBlock
{
    NSLog(@"Direct call to get is not allowed - use the threaded method");
    return nil;
}

And then change the block to this:

然后把block改成:

        _get_t = ^void (void){
            _weak_self->_getRunning = YES;
            NSArray *objects = [super get:getQuery 
                                usingClassCtor:ctor 
                                 withAuthBlock:authBlock];
            if (_weak_self.getBlockCb)
                _weak_self.getBlockCb(objects);
            _weak_self->_getRunning = NO;
        };

I have tried it and it works without doing a call to the [self getMethod], but will super be retained, properly released, etc? Yes I am using ARC. Would calling super within a block lead to any problem ? Is there a hack to get a __weak to super instead ?

我已经尝试过了,它可以在不调用[self getMethod]的情况下工作,但是super会被保留、被适当释放等等吗?是的,我在用弧线。在一个块中调用super会导致任何问题吗?有没有办法让弱队变成超级?

Alternatively, how can I disallow direct calls to [self getMethod] (which is inherited) and only use it internally ? I know that Objective-C doesn't exactly implement this, but I know there are tricks, such as declaring and implementing a method in the implementation file only.

另外,我如何才能不允许直接调用[self getMethod](它是继承的)而只在内部使用它呢?我知道Objective-C没有实现这个,但是我知道有一些技巧,比如只在实现文件中声明和实现一个方法。

EDIT#1:

编辑# 1:

I have tried with SEL & IMP and function pointers. Problem is that IMP and function pointers require as a parameter an instance, and this renders the hole point mute:

我试过SEL & IMP和函数指针。问题是IMP和函数指针需要作为一个参数实例,这使得空穴点静音:

NSString * (*getFuncPtr)(id,SEL,id,id) = (NSString * (*)(id,SEL,id,id))[super methodForSelector:@selector(sendObjectsPassingTest:withAuthBlock:)];
NSString *reply = getFuncPtr(_weak_self,@selector(sendObjectsPassingTest:withAuthBlock:),predicate,authBlock);

This simply calls the inherited method. Trying to use it with super simply gives an error. At this point I will go ahead and simply use super within the block, and try and profile to see if it leads to any retain cycle.

这只需调用继承的方法。尝试与super一起使用它只会产生一个错误。在这一点上,我将继续使用super在block中,并尝试和配置文件以查看它是否会导致任何保留周期。

EDIT#2:

编辑# 2:

Based on newacct's answer, this is what I ended up doing:

根据newacct的回答,这是我最后做的:

typedef NSArray * (* getFuncPtr)(id,SEL,id,id,id);
...
...
__weak SyncArrayMT *_weak_self = self;
_getMethod = (NSArray * (*)(id,SEL,id,id,id))[[[self class] superclass] instanceMethodForSelector:@selector(get:usingClassCtor:withAuthBlock:)];
_get_t = ^void (void){
   NSArray *objects = _weak_self->_getMethod(_weak_self,@selector(get:usingClassCtor:withAuthBlock:),getQuery,ctor,authBlock);
}

I am hoping this should avoid any retain cycles, although I haven't actually profiled it yet.

我希望这能避免任何保留周期,尽管我还没有实际地介绍它。

1 个解决方案

#1


5  

I know that calling [self methodName] from within a block will lead to a retain cycle.

我知道从一个块中调用[self methodName]将会导致一个retain cycle。

That is not true in general. The block will retain self, yes. But there will only be a "retain cycle" if self somehow retains the block. In this case, it does.

这在一般情况下是不正确的。block将保留self,是的。但是只有当self以某种方式保留了block时,才会有一个“retain cycle”。在这种情况下,确实如此。

but will super be retained

但将超级保留

Yes, self will be retained (super is a call on self with a different method lookup pathway).

是的,self将被保留(super是对self的调用,使用不同的方法查找路径)。

I have tried with SEL & IMP and function pointers. Problem is that IMP and function pointers require as a parameter an instance, and this renders the hole point mute:

我试过SEL & IMP和函数指针。问题是IMP和函数指针需要作为一个参数实例,这使得空穴点静音:

NSString * (*getFuncPtr)(id,SEL,id,id) = (NSString * (*)(id,SEL,id,id))[super methodForSelector:@selector(sendObjectsPassingTest:withAuthBlock:)]; 
NSString *reply = getFuncPtr(_weak_self,@selector(sendObjectsPassingTest:withAuthBlock:),predicate,authBlock); 

This simply calls the inherited method. Trying to use it with super simply gives an error. At this point I will go ahead and simply use super within the block, and try and profile to see if it leads to any retain cycle.

这只需调用继承的方法。尝试与super一起使用它只会产生一个错误。在这一点上,我将继续使用super在block中,并尝试和配置文件以查看它是否会导致任何保留周期。

There are many wrong points here. First, as said above, super is a call on self (there is no such thing as a super object), so it would be sufficient to get the IMP for the method in the superclass, and call it on self.

这里有很多错误的地方。首先,如上所述,super是对self的调用(不存在超对象),因此在超类中获取方法的IMP并调用self就足够了。

BUT, [super methodForSelector:... does not get the method in the superclass. It actually gets the method in this class. The super in [super methodForSelector:... affects which methodForSelector: method is called. However, no class ever overrides methodForSelector:, so there is actually no difference between [super methodForSelector:... and [self methodForSelector:.... As said above, super calls the method on self, so it still finds the method based on the class of the current object.

但是,[超级methodForSelector:…不获取超类中的方法。它会得到这个类中的方法。super in [super methodForSelector:…影响调用哪个methodForSelector: method。然而,从来没有类覆盖methodForSelector:,因此[super methodForSelector:…]之间实际上没有区别。和[自我methodForSelector:....如上所述,super对self调用方法,因此它仍然基于当前对象的类查找方法。

You can get the right IMP by using the class method +instanceMethodForSelector::

您可以使用类方法+instanceMethodForSelector:::

NSString *(*getFuncPtr)(id,SEL,id,id) = (NSString * (*)(id,SEL,id,id))[[[self class] superclass] instanceMethodForSelector:@selector(sendObjectsPassingTest:withAuthBlock:)];

However, using the above will not work correctly if the current object is an instance of a subclass, because then [self class] will be the subclass. So to make sure it does what we want, we need to hard-code the name of our current class, or the superclass:

但是,如果当前对象是子类的实例,则不能正确地使用上面的方法,因为[self class]将是子类。因此,为了确保它实现我们想要的功能,我们需要硬编码当前类或超类的名称:

NSString *(*getFuncPtr)(id,SEL,id,id) = (NSString * (*)(id,SEL,id,id))[[SyncArrayMT superclass] instanceMethodForSelector:@selector(sendObjectsPassingTest:withAuthBlock:)];
NSString *reply = getFuncPtr(_weak_self,@selector(sendObjectsPassingTest:withAuthBlock:),predicate,authBlock);

It is also possible to do it using objc_msgSendSuper directly, but that function is not really that easy to use either. So I think you should stick with the IMP approach above.

也可以直接使用objc_msgSendSuper,但是这个函数也不是很容易使用。所以我认为你应该坚持上面的IMP方法。

#1


5  

I know that calling [self methodName] from within a block will lead to a retain cycle.

我知道从一个块中调用[self methodName]将会导致一个retain cycle。

That is not true in general. The block will retain self, yes. But there will only be a "retain cycle" if self somehow retains the block. In this case, it does.

这在一般情况下是不正确的。block将保留self,是的。但是只有当self以某种方式保留了block时,才会有一个“retain cycle”。在这种情况下,确实如此。

but will super be retained

但将超级保留

Yes, self will be retained (super is a call on self with a different method lookup pathway).

是的,self将被保留(super是对self的调用,使用不同的方法查找路径)。

I have tried with SEL & IMP and function pointers. Problem is that IMP and function pointers require as a parameter an instance, and this renders the hole point mute:

我试过SEL & IMP和函数指针。问题是IMP和函数指针需要作为一个参数实例,这使得空穴点静音:

NSString * (*getFuncPtr)(id,SEL,id,id) = (NSString * (*)(id,SEL,id,id))[super methodForSelector:@selector(sendObjectsPassingTest:withAuthBlock:)]; 
NSString *reply = getFuncPtr(_weak_self,@selector(sendObjectsPassingTest:withAuthBlock:),predicate,authBlock); 

This simply calls the inherited method. Trying to use it with super simply gives an error. At this point I will go ahead and simply use super within the block, and try and profile to see if it leads to any retain cycle.

这只需调用继承的方法。尝试与super一起使用它只会产生一个错误。在这一点上,我将继续使用super在block中,并尝试和配置文件以查看它是否会导致任何保留周期。

There are many wrong points here. First, as said above, super is a call on self (there is no such thing as a super object), so it would be sufficient to get the IMP for the method in the superclass, and call it on self.

这里有很多错误的地方。首先,如上所述,super是对self的调用(不存在超对象),因此在超类中获取方法的IMP并调用self就足够了。

BUT, [super methodForSelector:... does not get the method in the superclass. It actually gets the method in this class. The super in [super methodForSelector:... affects which methodForSelector: method is called. However, no class ever overrides methodForSelector:, so there is actually no difference between [super methodForSelector:... and [self methodForSelector:.... As said above, super calls the method on self, so it still finds the method based on the class of the current object.

但是,[超级methodForSelector:…不获取超类中的方法。它会得到这个类中的方法。super in [super methodForSelector:…影响调用哪个methodForSelector: method。然而,从来没有类覆盖methodForSelector:,因此[super methodForSelector:…]之间实际上没有区别。和[自我methodForSelector:....如上所述,super对self调用方法,因此它仍然基于当前对象的类查找方法。

You can get the right IMP by using the class method +instanceMethodForSelector::

您可以使用类方法+instanceMethodForSelector:::

NSString *(*getFuncPtr)(id,SEL,id,id) = (NSString * (*)(id,SEL,id,id))[[[self class] superclass] instanceMethodForSelector:@selector(sendObjectsPassingTest:withAuthBlock:)];

However, using the above will not work correctly if the current object is an instance of a subclass, because then [self class] will be the subclass. So to make sure it does what we want, we need to hard-code the name of our current class, or the superclass:

但是,如果当前对象是子类的实例,则不能正确地使用上面的方法,因为[self class]将是子类。因此,为了确保它实现我们想要的功能,我们需要硬编码当前类或超类的名称:

NSString *(*getFuncPtr)(id,SEL,id,id) = (NSString * (*)(id,SEL,id,id))[[SyncArrayMT superclass] instanceMethodForSelector:@selector(sendObjectsPassingTest:withAuthBlock:)];
NSString *reply = getFuncPtr(_weak_self,@selector(sendObjectsPassingTest:withAuthBlock:),predicate,authBlock);

It is also possible to do it using objc_msgSendSuper directly, but that function is not really that easy to use either. So I think you should stick with the IMP approach above.

也可以直接使用objc_msgSendSuper,但是这个函数也不是很容易使用。所以我认为你应该坚持上面的IMP方法。