解决保留周期:当我们已经有__weak * weakSelf时,是否真的有理由添加__strong * strongSelf?

时间:2014-04-10 18:49:53

标签: objective-c automatic-ref-counting objective-c-blocks

如果我将类Foo声明为:

,该怎么办?
@interface Foo : NSObject
@property (copy, nonatomic) void (^aBlock)(void);
- (void)bar;
@end

@implementation Foo
- (void)bar {}
@end

我在Foo实例中存储了一个捕获此实例的块:

Foo *foo = [Foo new];

__weak Foo *weakFoo = foo;
foo.aBlock = ^{
    __strong Foo *strongFoo = weakFoo; <--- does this __strong Foo * really makes sense?

    [strongFoo bar];
}

... then somewhere later use foo.aBlock()

或者我只是这样做:

Foo *foo = [Foo new];

__weak Foo *weakFoo = foo;
foo.aBlock = ^{
    [weakFoo bar];
}

... then somewhere later use foo.aBlock()

添加__strong *strongFoo = weakFoo是否有意义,或仅仅在块中使用[weakFoo bar]是否足够?

3 个答案:

答案 0 :(得分:5)

在您的示例中,并非绝对有必要再次在块内强化引用:weakFoo将为nil且方法未被调用,或者它是&#39; sa有效参考,并将在此通话期间保持有效。

但请考虑以下代码段:

Foo *foo = [Foo new];

__weak Foo *weakFoo = foo;
foo.aBlock = ^{
    [weakFoo bar];
    [weakFoo baz];
}

... then somewhere later use foo.aBlock()

现在您引入了竞争条件,其中weakFoo可能对第一次呼叫有效,但nil用于第二次呼叫。这几乎不是你想要的。在这种情况下,请使用__strong Foo *foo = weakFoo;确保两次调用都具有相同的值。

修改

以下是一个如何触发这种竞争条件的示例:

@interface BlockHolder : NSObject
@property (nonatomic, copy) void(^block)(void);
@end

@implementation BlockHolder
@end


int main(int argc, const char * argv[])
{
    @autoreleasepool {
        BlockHolder *holder = [[BlockHolder alloc] init];

        __weak BlockHolder *weakHolder = holder;
        holder.block = ^{
            for (int i = 0; i < 10; i++) {
                NSLog(@"weakHolder is %@", weakHolder);
                sleep(1);
            }
        };

        dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);

        dispatch_async(queue, holder.block);

        sleep(2); // give the queue some time to start executing the block
        holder = nil;

        dispatch_sync(queue, ^{}); // wait for the queue to finish
    }
    return 0;
}

答案 1 :(得分:2)

您应该使用:

__weak NSObject * weakFoo = foo;  // Assign foo to weakFoo to avoid retain cycles
foo.block =
^{
    NSObject * strongFoo = weakFoo; // By default it is strong, no need for __strong prefix. Hold the weakFoo to make sure it is not nil out while block is executing.
    if (strongFoo != nil) // Nil check if you want to execute code in block if non-nil.
    {
       [strongFoo useIt];
    }
}

答案 2 :(得分:1)

有一个原因。问题是,__weak引用不会在块期间使对象保持活动。因此,例如,如果您有一个异步块,weakSelf可能会在块的执行中途消失(因为弱引用不会使对象保持活动状态)。所以我们在块中使用strongSelf来确保对象足够长,以便在块中一直使用它。