块中的强引用,会被保留吗?

Strong reference in the block, it will be retained?

我从公司文档中发现了这段代码:

__weak __typeof(self)weakSelf = self;
dispatch_async(dispatch_get_main_queue(),
^{
      __strong __typeof(weakSelf)strongSelf = weakSelf; 
      // Do stuff
});

会保留吗?

块外的弱拷贝就够了

__weak __typeof(self)weakSelf = self;
dispatch_async(dispatch_get_main_queue(),
^{
      // silliness: __strong __typeof(weakSelf)strongSelf = weakSelf; 
      // Do stuff with weakSelf here
});

其实这样也可以:

dispatch_async(dispatch_get_main_queue(),
^{
    // self self self, go ahead and mention self here
});

绝对不能做的事情是在某个地方复制那个块,并在块中的某个地方提到它。

@property (nonatomic, strong) void (^myBlock)(void);

self.myBlock = ^void(void) {
    // don't mention self here!
};

想法是块保留它们提到的对象,我们试图避免循环,所以 object -> block -> another_object 很好,但 object -> block -> another_object -> any amount of indirection -> object 是一个循环,这很糟糕。

循环是不好的,因为如果一个对象保留在别处就不能被释放,所以它保留的东西也不能被释放。如果两个东西相互保留,那么它们都会被卡住,无法解除分配,因为每个东西都被某些东西保留了。

编辑 直到今天我还误解了弱变量的强副本并不总是愚蠢的。它可能是相关的,但有意义的情况是高度合格的)。

如果你使用这个:

__weak __typeof(self)weakSelf = self;
dispatch_async(dispatch_get_main_queue(),
^{ 
    // Do stuff
});

在执行块时,Self 可能会被释放。所以我们最好将 weakSelf 转换为 strongSelf 以确保 self 保留在内存中,直到块执行完毕。

使用__strong __typeof(weakSelf)strongSelf = weakSelf后,self 将引用本地堆栈变量。所以不会保留。

在一个块中捕获 weak 个引用有两个原因。

  1. 避免循环保留

  2. 创造 no-op 个情况。

前者已讨论ad-nauseum。第二个更有趣。

例子

The block in question is a completion handler for an image download. When the download is complete, it is to be displayed in an image view.

如果图像视图已经被解除分配(比如用户已切换到新视图),则不需要执行任何操作。没有保留循环的危险,因为图像视图没有引用块。但是,捕获 weak 引用允许在块执行之前释放图像视图。因此,如果用户在下载图像之前切换视图,该块最终什么都不做,因为它的 weak 引用已经被 niled。图像视图是否在块的执行过程中被释放也无关紧要,因为它只是将图像视图上的操作变成 no-ops,而不是将整个块变成 no-op。

但是,有时块需要 no-op 行为,但前提是引用在开始时已经 nil(或到达代码路径中的某个点)。如果在块执行时 object 处于活动状态,则块必须完整执行。它无法停止 half-way,因为 object 已在其他线程上释放。

例子

The purpose of the completion block is to add a caption, defined by a string, to the image. If the string has been deallocated already, no caption is to be added. However, if the string is still live when post-processing begins, it must remain live to avoid trying to create an attributed string with a nil reference, because that leads to a crash.

在这种情况下,使用 weak 引用捕获字符串是合适的,这样它就可以被其他线程释放(导致没有标题)。但是,在块中使用该字符串之前,必须 strongly 捕获它以避免在创建属性字符串时发生崩溃。