何时在具有ARC的对象引用上使用__block关键字

6

我理解在块中要看到对标量变量的更新,需要使用__block存储类型,但是什么时候需要在对象上使用它呢?如果捕获了一个自我引用以在块中使用,我认为应该使用__weak,但我不知道何时需要在普通对象上实际使用__block存储类型。


我特别想知道:在使用ARC时,当非标量、非self对象引用需要使用__block存储类型时应该怎么做?如果其他链接回答了这个问题,那我可能错过了。 - chinabuffet
@chinabuffet:对于标量类型和对象指针类型,它们的工作方式没有任何区别。 - newacct
2个回答

17
如果你想要在块内部的代码中改变标量变量的值,则需要使用__block。捕获的标量在块内部显示为const,因此无法更改。如果你有一个对象的指针,则相同的区别适用 - 捕获的指针本身将是const指针,因此无法修改,但是所指向的对象可以通过块内部的代码进行修改。如果你想要更改所指向的对象,则指针本身必须更改,因此指针必须使用__block类型声明。永远不需要将对象本身声明为__block,只需要将指向对象的指针声明为__block,并且仅在指针必须更改时才需要。
如果你拥有正确的心理模型,块就不会那么困惑了。重要的是要知道,块最初是在堆栈上分配的,在词法范围被销毁时随着堆栈帧被弹出而消失。如果要使块在创建块的词法范围的生存期过后仍然存在,请使用Block_copy()或发送-copy消息将其移到堆上。当一个块被复制到堆上时,所有捕获的const变量都会一起复制,这些const变量指向的任何对象都将被保留。当块从堆上移除时,所有被const变量所指向的对象都将被释放。 __block变量在编译器使用的“底层”中有一个额外的间接层(你看不见),包含在该块中,因此当该块被复制到堆上时,捕获的__block变量也会被复制,并且隐形指针会调整为指向这些__block变量的新堆位置。这意味着__block变量的地址可以改变,因此如果你使用该地址,则需要小心。你还可以看到__block变量在某种意义上是“在”块的外部生存的,因此可以从块外部的代码中读取和修改这些变量。

我很简要地介绍了一下,但你可以在以下链接中找到更好的解释,按照复杂度递增的顺序列出:

http://ios-blog.co.uk/tutorials/programming-with-blocks-an-overview/

http://www.cocoawithlove.com/2009/10/how-blocks-are-implemented-and.html

http://www.mikeash.com/pyblog/friday-qa-2011-06-03-objective-c-blocks-vs-c0x-lambdas-fight.html


那么如果该块作为引用传递到某个地方,接收该块作为参数的任何内容是否需要立即复制该块以便将来使用? - chinabuffet
如果块引用可以超出定义块的词法作用域(堆栈帧),则必须将其复制以将块对象移动到堆上。我认为在创建块时进行复制比在远离创建位置发生这些复制更安全。 - Fred
在 Objective-C 的 for-in 循环中是否需要使用 __block - Developer Sheldon

0

它们用于函数级变量。这些变量在块内(和封闭作用域)是可变的,并且如果任何引用块被复制到堆中,则会被保留。使用 __block 存储修饰符声明的封闭词法作用域本地变量是通过引用提供的,因此是可变的。任何更改都会反映在封闭词法作用域中,包括在同一封闭词法作用域内定义的任何其他块。

__block 变量存在于变量的词法作用域和所有块及在变量的词法作用域内声明或创建的块副本之间共享的存储器中。因此,如果在帧结束后任何块的副本存活下来(例如,通过将其排队等待以后执行),则存储器将在堆栈帧的销毁后仍然存在。因此,在需要在块内修改对象或在堆栈帧的销毁后需要该对象时,请使用它们。


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