3

我有点苦恼。我知道从块中调用 [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。在一个块内调用 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);
}

我希望这应该避免任何保留周期,尽管我还没有真正分析它。

4

1 回答 1

5

我知道从块中调用 [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:...没有得到超类中的方法。它实际上得到了这个类中的方法。superin[super methodForSelector:...影响methodForSelector:调用哪个方法。但是,没有任何类会覆盖,因此和methodForSelector:之间实际上没有区别。如上所述,调用方法 on ,所以还是根据当前对象的类来查找方法。[super methodForSelector:...[self methodForSelector:...superself

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

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

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

于 2013-01-03T03:30:56.897 回答