__weak引用和__block引用有什么区别?

82
我正在阅读Xcode的文档,这里有一些令我困惑的内容:
__block typeof(self) tmpSelf = self;
[self methodThatTakesABlock:^ {    [tmpSelf doSomething];
}];

以下内容摘自官方文档:

块会对其捕获的变量形成强引用。如果在块内使用了self,块会对self形成强引用,因此如果self也对该块形成了强引用(通常情况下是这样),就会形成强引用循环。为避免循环,需要在块外创建一个对self的弱引用(或__block引用),就像上面的示例一样。

我不理解'weak (or __block)'是什么意思?

是的。

__block typeof(self) tmpSelf = self;

__weak typeof(self) tmpSelf = self;

这里完全一样吗?

我在文档中找到了另一段:

注意:在垃圾回收环境中,如果您将__weak__block修饰符同时应用于一个变量,则该块不会确保该变量保持存活。

所以,我完全被困惑了。

4个回答

110

关于 __block 的文档说明:

__block 变量存在于变量的词法作用域和在该变量的词法作用域中声明或创建的所有块及块副本之间共享的存储器中。因此,如果在帧结束后(例如,通过在某个地方排队以供稍后执行)存在任何块的副本,则存储器将在堆栈帧的销毁后继续存在。给定词法作用域中的多个块可以同时使用共享变量。

关于 __weak 的文档说明:

__weak 指定不保持引用对象存活的引用。当没有强引用指向该对象时,弱引用会被设置为 nil。

所以它们在技术上是不同的东西。__block 用于防止将变量从外部作用域复制到块作用域中。__weak 是一个自我限定的弱指针。

需要注意的是,我说“技术上”,因为对于您的情况它们将(几乎)做相同的事情。唯一的区别是是否使用 ARC。如果您的项目使用 ARC,并且仅适用于 iOS4.3 及以上版本,请使用 __weak。它确保如果全局作用域引用在某种方式下被释放,引用会被设置为 nil。如果您的项目不使用 ARC 或适用于旧的操作系统版本,请使用 __block。

这里有一个微妙的区别,请确保你理解它。

编辑:另一个谜题的部分是 __unsafe_unretained。这个修饰符与 __weak 几乎相同,但适用于 4.3 之前的运行时环境。但是,它不会被设置为 nil,并且可能会导致悬空指针。


1
这个对使用ARC的iOS7仍然适用吗?我运行了一个分析器,发现即使我没有使用__block或__weak并在块中引用self,我的控制器仍然被释放了。 - Jay Q.
1
想查看文档的人可以访问以下链接:https://developer.apple.com/library/ios/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html,https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Blocks/Articles/bxVariables.html - Joon Hong
1
他们一起使用如何?__block _weak NSString *strEg; - CyberMew

6
在手动引用计数模式下,__block id x; 的效果是不保留 x。在ARC模式下,__block id x; 默认保留x(就像所有其他值一样)。要在ARC下获得手动引用计数模式的行为,可以使用__unsafe_unretained __block id x;。然而,正如__unsafe_unretained所示,拥有非保留变量是危险的(因为它可能悬空),因此不建议这样做。两个更好的选择是使用__weak(如果您不需要支持iOS 4或OS X v10.6),或将__block值设置为nil以打破保留循环。 苹果文档

1

在块中使用self时,应该使用__weak而不是__block,因为它可能会保留self。

如果需要强引用self,则可以像这样使用:

__weak typeof(self) *weakSelf = self;
[self methodThatTakesABlock:^{
    if (weakSelf) {
        __strong typeof(self) *strongSelf = weakSelf;
        [strongSelf doSomething];
    }
}];

0

除了其他关于__block__weak的答案之外,在你的情况下还有另一种避免保留循环的方法。

@weakify(self);
[self methodThatTakesABlock:^ {    @strongify(self);    [self doSomething];
}];

关于 @Weakify 和 @Strongify 宏的更多信息


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