我应该在嵌套块中使用weakSelf吗?

时间:2016-08-08 18:07:47

标签: ios objective-c objective-c-blocks retain-cycle

我试图在Objective C中正确避免使用块保留循环,并且不确定是否有嵌套块。

如果我写一个这样的简单块:

[self doSomethingWithBlock:^{
    [self doSomethingElse];
}];

编译器捕获并警告我这可能导致保留周期。我按如下方式更改它以避免循环:

__weak __typeof(self)weakSelf = self;
[self doSomethingWithBlock:^{
    __strong __typeof(weakSelf)strongSelf = weakSelf;
    [strongSelf doSomethingElse];
}];

当我写这样的东西时:

[self doSomethingWithBlock:^(MyObject* object){
    [object doSomethingElseWithBlock:^{
        [self doYetAnotherThing];
    }];
}];

编译器很高兴,但我不相信它是安全的。尽管介于两者之间存在中介object,但它在概念上仍然与上面相同,但现在它是一个有3个保留的循环。

应该是这样吗?

[self doSomethingWithBlock:^(MyObject* object){
    __weak __typeof(self)weakSelf = self;
    [object doSomethingElseWithBlock:^{
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        [strongSelf doYetAnotherThing];
    }];
}];

或者喜欢这个?

__weak __typeof(self)weakSelf = self;
[self doSomethingWithBlock:^(MyObject* object){
    [object doSomethingElseWithBlock:^{
        __strong __typeof(weakSelf)strongSelf = weakSelf;
        [strongSelf doYetAnotherThing];
    }];
}];

4 个答案:

答案 0 :(得分:5)

在这种情况下,您不必担心循环引用。你担心的是对象自身不再需要的情况,但在嵌套块中使用self会使它不必要地活着。例如,如果您的视图控制器在屏幕移除视图时应该消失,但您下载了要在控制器视图中显示的图像。如果图像在视图消失后很长时间到达,则您不希望视图控制器再次存在。

最好是

__weak typeof (self) weakSelf = self;

在调用最外层方法之前。然后在每个应该使用self的块中添加

typeof (self) strongSelf = weakSelf;

并在该块中使用strongSelf。根据具体情况,您可能需要检查strongSelf当时是否为nil,但是当它为nil时发送消息给strongSelf无效,所以如果您只是发送消息并获取或设置属性,那么不需要检查零。

如果你不这样做会怎么样?不同之处在于,如果你在任何地方(或者只是在最里面的块中)使用self,那么self可以不必要地保持在最里面的块中。

答案 1 :(得分:0)

查看question的答案。

我这样做

__weak typeof (self) weakSelf = self;
[self doSomethingWithBlock:^(MyObject* object){
    [object doSomethingElseWithBlock:^{
        [weakSelf doYetAnotherThing];
    }];
}];

答案 2 :(得分:0)

Xcode 8 beta 4强调了self关键字,并警告在块内使用它的可能保留周期。

每个Apple Developer Connection的Objective-C编程(使用块):

  

在捕捉自我时避免强烈的参考周期如果需要   在块中捕获self,例如在定义回调块时,它就是   重要的是考虑内存管理的影响。

     

Blocks保持对任何捕获对象的强引用,包括   自我,这意味着很容易得到一个强大的参考   例如,如果对象维护块的复制属性,则循环   捕获自我:

@interface XYZBlockKeeper : NSObject
@property (copy) void (^block)(void);
@end
@implementation XYZBlockKeeper
- (void)configureBlock {
    self.block = ^{
        [self doSomething];    // capturing a strong reference to self
                               // creates a strong reference cycle
    };
}
...
@end
  

编译器会警告你这样一个简单的例子,但更多   复杂的例子可能涉及多个强引用   创建循环的对象,使诊断更加困难。

     

要避免此问题,最佳做法是捕获弱引用   自我,像这样:

- (void)configureBlock {
    XYZBlockKeeper * __weak weakSelf = self;
    self.block = ^{
        [weakSelf doSomething];   // capture the weak reference
                                  // to avoid the reference cycle
    }
}
  

通过捕获指向self的弱指针,该块将不会维持   强烈的关系回到XYZBlockKeeper对象。如果那个对象   在调用块之前释放,weakSelf指针将被释放   只需设置为零。

据报道,该网站提供了一种方法,可以在块内使用时使self关键字变弱;它还提供了返回弱自我或类对象的指令,这些对象以前强大,强大:

https://coderwall.com/p/vaj4tg/making-all-self-references-in-blocks-weak-by-default

答案 3 :(得分:0)

你不应该仅仅因为你从编译器收到警告而捕获一些东西;编译器警告只是猜测;它不知道你调用的方法是如何引用的。只有在了解了引用的体系结构并确定存在循环并确定捕获弱引用时仍然保留预期行为时,才应执行此操作。您还没有向我们展示-doSomethingWithBlock:的代码。它只会在该方法内部创建一个保留周期,它会将块分配给self的属性或实例变量。它会这样做吗?如果没有,那么就没有保留周期,并且外部块无法轻微捕获self

假设弱块捕获self的外部块是正确的,外部块强烈捕获self的示例是不可能的。剩下的问题是内部区块是否应该强烈地捕获self(或self的任何版本,例如strongSelf)。换句话说,你是否会这样做:

__weak __typeof(self) weakSelf = self;
[self doSomethingWithBlock:^(MyObject* object){
    __strong __typeof(weakSelf) strongSelf = weakSelf;
    if (strongSelf) {
        [object doSomethingElseWithBlock:^{
            [strongSelf doYetAnotherThing];
        }];
    }
}];

或类似的东西:

__weak __typeof(self) weakSelf = self;
[self doSomethingWithBlock:^(MyObject* object){
    __strong __typeof(weakSelf) strongSelf = weakSelf;
    if (strongSelf) {
        [object doSomethingElseWithBlock:^{
            __strong __typeof(weakSelf) strongSelf = weakSelf;
            if (strongSelf) {
                [strongSelf doYetAnotherThing];
            }
        }];
    }
}];

同样,要确定的主要问题是,如果内部块强烈捕获self,是否存在保留周期。如果[object doSomethingElseWithBlock:...以某种方式将块分配给self的属性或实例变量,则只会有一个保留周期。但怎么可能呢?该方法在object上调用,而不是self。该方法无法以任何方式获得self。除非存在复杂的事情,否则该方法不会分配给self的属性或实例变量,因此不太可能创建保留周期。这意味着弱块捕获self的内部块不是防止保留周期所必需的。

但是内部块是否弱或强地捕获self可能会影响行为。也就是说,如果内部块弱地捕获self,则self可以在块运行时被释放,在这种情况下[strongSelf doYetAnotherThing];将不会被执行,而如果内部块被捕获{ {1}}强烈地,它会使self保持活跃状态​​,self将被执行。所以它取决于[strongSelf doYetAnotherThing];的作用。如果它在-doYetAnotherThing上执行某些UI操作(UI视图或其他内容),那么无论您是在不再显示的视图上执行该操作都不会产生影响。但是,如果它例如向网络发送某些东西,那么它是否被执行可以产生很大的不同。