从GCD dispatch_async块中调用super:它安全吗?

时间:2013-01-02 21:16:42

标签: objective-c objective-c-blocks superclass method-hiding retain-cycle

我有点腌渍。我知道从块中调用[self methodName]会导致保留周期。

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

当前代码:

 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;
        };
    }

确实如此,它称之为[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;
}

然后将块更改为:

        _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;
        };

我已经尝试了它并且它可以在不调用[self getMethod]的情况下工作,但是会超级保留,正确释放等等吗?是的我正在使用ARC。在一个街区内召唤超级会导致任何问题吗?是否有一个hack来获得__weak而不是超级?

或者,如何禁止直接调用[self getMethod](继承)并仅在内部使用它? 我知道Objective-C没有完全实现这一点,但我知道有一些技巧,比如在实现文件中声明和实现一个方法。

编辑#1:

我尝试过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);

这只是调用继承的方法。尝试使用超级简单的方法会产生错误。在这一点上,我将继续并在块内简单地使用super,并尝试分析它是否会导致任何保留周期。

编辑#2:

根据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);
}

我希望这应该避免任何保留周期,虽然我还没有实际描述它。

1 个答案:

答案 0 :(得分:5)

  

我知道从一个块中调用[self methodName]会导致   保留周期。

总的来说,情况并非如此。该块将保留self,是的。但如果self以某种方式保留该块,则只会有“保留周期”。在这种情况下,确实如此。

  

但将超级保留

是的,self将被保留(superself上的呼叫,具有不同的方法查找路径。)

  

我尝试过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); 
     

这只是调用继承的方法。尝试使用超级简单的方法会产生错误。在这一点上,我将继续并在块内简单地使用super,并尝试分析它是否会导致任何保留周期。

这里有很多错误点。首先,如上所述,super是对self的调用(没有super对象之类的东西),所以只需要获取方法的IMP即可。超类,并在self上调用它。

但是,[super methodForSelector:... 获取超类中的方法。它实际上是在 this 类中获取方法。 super中的[super methodForSelector:...会影响调用哪个methodForSelector:方法。但是,任何课程都不会覆盖methodForSelector:,因此[super methodForSelector:...[self methodForSelector:...之间实际上没有任何区别。如上所述,super会在self上调用该方法,因此它仍会根据当前对象的类找到该方法。

您可以使用类方法+instanceMethodForSelector:获得正确的IMP:

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

但是,如果当前对象是子类的实例,则使用上述方法将无法正常工作,因为[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);

也可以直接使用objc_msgSendSuper来完成,但这个功能也不是那么容易使用。所以我认为你应该坚持上面的IMP方法。