在块内引用弱引用的强引用

9
为什么在块内部需要一个强引用指向一个弱引用?
我理解在块内使用弱引用可以避免保留循环。但为什么必须再次有一个强引用指向弱引用呢?
背景: 正如Mason所描述的那样,这是最佳实践。
示例:
__weak typeof(self) weakSelf = self;
void (^someBlock)(id) = ^(id data){
    typeof(self) strongSelf = weakSelf;
    // code using strongSelf
});

在块内部重新强制指针并不是必要的。 - holex
1
如果您使用libextobj,一种更加优美和简洁的方法是使用@weakify(self)@strongify(self) - lukas_o
1
可能会更美观,但这不是官方语言的一部分。 - holex
@holex 是的,“在大多数情况下”是这样的。假设在代码块中我调用了 [weakSelf methodA] 和 [weakSelf methodB],它们将把一些共享的 BOOL A 从 false 改为 true,同时将另一些共享的 BOOL B 从 false 改为 true。使用 weak Self 可能会使 BOOL A 保持为 true 而 BOOL B 仍然为 false。 - BangOperator
@BangOperator,是的,这种情况在实践中几乎不会发生,但显然这取决于所选择的设计模式和架构。我见过一些非常有创意的设计,就像你刚才描述的假设情况一样 - 所以,我明白了大意。 - holex
显示剩余2条评论
2个回答

15

想象一下,对自身的最后一个强引用被持有在与您的代码块运行的线程不同的线程上。

现在发生了这样的情况:

__weak typeof(self) weakSelf = self;
void (^someBlock)(id) = ^(id data){
    if (weakSelf != nil) {
       // last remaining strong reference released by another thread. 
       // weakSelf is now set to nil.
       [myArray addObject:weakSelf];
    }
});

将nil添加到数组中将会导致NSInvalidArgument异常。

在使用之前将引用设置为强引用可以消除潜在的竞态条件,并确保指针始终指向同一个对象。

如果您可以百分之百确定对象只会被一个线程引用,那么做这件事并不是严格必要的。但是,这样做是一种不好的做法。


1
如果在这些情况下让应用程序崩溃,将有助于更轻松地找到错误的原因和释放对象的位置,而不管任何块仍然需要它。 - holex
1
在另一个线程上持有弱引用时允许释放对象存在合理的原因。当然,您可能希望断言指针不为nil,但在weakSelf上这样做会遇到相同的竞态条件。 - Chris Devereux
2
如果使用弱引用的唯一原因是为了避免保留循环,那么在块执行之前或期间弱引用变为nil的任何情况都可以被认为是一个错误。 - ipmcc
在这种情况下,您仍然希望可预测地失败。引入竞态条件并不能改善调试体验... - Chris Devereux
@ChrisDevereux 你太棒了!解释得非常详细,而且这个是最优的!Swift中也是这样吗?我们在哪里使用捕获列表?如果是的话,请告诉如何将 [weak self] 转换为 self。 - nr5

6

这并非必要,但一般的想法是确保在块执行期间,weakSelf所指向的对象不会被释放。创建强引用的副作用是保留该对象。当强引用超出范围时,ARC将释放该保留。这主要是为了防御。一般来说,在块执行期间,您应该努力提供其他(更好的)保证,以确保系统保持稳定。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接