【问题标题】:calling super from within a GCD dispatch_async block: is it safe?从 GCD dispatch_async 块中调用 super:安全吗?
【发布时间】:2012-12-17 05:57:28
【问题描述】:

我有点吃不消了。我知道从一个块中调用 [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] 的情况下工作,但是 super 会被保留、正确释放等吗?是的,我正在使用 ARC。在一个块内调用 super 会导致任何问题吗?有没有办法让 __weak 改为 super ?

或者,我怎样才能禁止直接调用 [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 一起使用只会产生错误。在这一点上,我将继续并简单地在块内使用 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);
}

我希望这可以避免任何保留周期,尽管我还没有真正对其进行分析。

【问题讨论】:

  • 为什么不能只声明一个指向超类的weakSelf 指针?你知道吗,比如__weak __block NSObject *weakSuper = self;
  • 这是一个有趣的想法,让我测试一下。
  • 不,它不起作用,由于某种原因,继承的方法被调用,而不是来自超类的方法。似乎只有 [super method] 将调用委托给正确的类。除非我做错了。
  • CodaFi 的评论有错字,应该是:__weak __block NSObject *weakSuper = super;
  • @AndrewTetlaw:不,super 在 Objective-C 中不是一个有效的表达式(与大多数其他 OO 语言一样,例如 Java)。它只能用在消息调用表达式的左侧。

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


【解决方案1】:

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

这通常不是真的。该块将保留self,是的。但是,如果self 以某种方式保留该块,则只会有一个“保留周期”。在这种情况下,确实如此。

但会超级保留

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

我尝试过使用 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,并尝试分析它是否会导致任何保留循环。

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

但是,[super methodForSelector:... 确实没有获得超类中的方法。它实际上获取了 this 类中的方法。 [super methodForSelector:... 中的 super 影响调用哪个 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 方法。

【讨论】:

  • 非常感谢,这更有意义。我最终根据您的方法做了一些事情。请记住,仪器在使用 [super] 时没有显示任何保留问题,但我可能是错的。
猜你喜欢
  • 2011-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-15
  • 1970-01-01
相关资源
最近更新 更多