【问题标题】:Does object need (a) `__block` modifier or (b) weak reference, when accessed from within a block in Objective-C?当从 Objective-C 的块中访问时,对象是否需要 (a) `__block` 修饰符或 (b) 弱引用?
【发布时间】:2016-09-04 02:04:08
【问题描述】:

在 Objective-C(从 Xcode 7 开始)中,用于基元的 __block 修饰符在 Stack Overflow(例如 here 和 Apple here in the Blocks Programming Guide)中有明确的解释。如果没有标签,则会捕获图元的副本,并且无法从块内修改原始图元。有了标签,就没有副本了,原来的可以在块内修改。

但是对于与指向对象的指针一起使用,情况并没有得到很好的解释。 Here Apple 说“对自身进行了强引用”“对变量进行了强引用”。但是在页面的最后,Apple 说:

要覆盖特定对象变量的这种行为,您可以使用 __block 存储类型修饰符对其进行标记。

“覆盖此行为”是什么意思?

更复杂的事情是 Stack Overflow 上的一些帖子谈到在从块中调用对象时进行弱引用。

不是试图建立一个对象。我只想从块中修改现有对象的状态。我相信该块是在同一个线程中同步调用的。

想象一下:

Clicker* myClicker = [[Clicker alloc] init] ;
…
// Declare a block.
void (^myBlock)( );

// Populate the block.
myBlock = ^ void ( ) {
    [myClicker click] ; // Updating some state in that object, such as incrementing a counter number or adding an element to a collection.
};

// Call `myBlock` synchronously (same thread) from some other code.
…  // … invokes `myBlock` repeatedly …

我的问题:

  • 应该如何使用__block修饰符修改该代码?

  • 应该如何修改带有弱引用的代码?

  • 还有哪些其他问题适用于从块内修改的对象状态?

【问题讨论】:

  • 关键问题是:区块由什么拥有?

标签: ios objective-c scope objective-c-blocks


【解决方案1】:

你引用了有点过时的Blocks Programming Topics,它说:

要覆盖特定对象变量的此行为,您可以使用__block 存储类型修饰符对其进行标记。

该文档可以追溯到手动引用计数时代,早于ARC。在手动引用计数代码中,您可以使用__block 来避免为块内引用的对象建立strong reference

但正如Transitioning to ARC Release Notes 中所述,ARC 改变了这种行为。我们现在在不希望对块中引用的对象建立强引用的情况下使用weak。 (在您知道生成的悬空指针没有问题的特殊情况下,您可以使用unsafe_unretained。)

所以,在处理要在块内变异的基本类型时,请继续使用__block。但是在 ARC 下处理块中的对象时,__block 限定符通常不会进入讨论。问题只是你想要一个强引用还是一个弱引用(或一个不安全、未保留的引用)。这在很大程度上取决于您的应用程序的object graph,即(a)您是否需要弱/未保留的引用来防止强引用循环;或 (b) 您不希望某个异步执行的块不必要地延长该块中引用的某些对象的生命周期。这些情况似乎都不是这里的情况。

【讨论】:

    【解决方案2】:

    首先,__block 的基本点对于所有类型的变量(原始变量、对象指针变量和所有其他变量)都是相同的——__block 使变量在外部范围和捕获它的块的作用域,以便在一个作用域中对变量的赋值 (=) 在所有其他作用域中都可以看到。

    所以如果你不使用__block,如果你在创建块后分配给一个对象指针变量以指向块外的另一个对象,那么块将看不到对变量的赋值,并且仍然会看到它指向它在创建块时指向的对象。相反,在块内,您将无法分配给变量。如果你确实使用了__block,那么分配给变量以指向另一个对象,无论是在块内部还是外部,都将反映在其他作用域中。

    请注意,对象状态的突变与分配给变量无关。您可以通过在指向对象的指针上调用 mutating 方法来更改对象的状态,或者您可以使用 -> 语法使用指向对象的指针直接更改字段。这些都不涉及分配给保存指向对象的指针的变量。另一方面,分配给持有指向对象指针的变量只会使指针指向另一个对象。它不会改变任何对象。

    您正在阅读的有关强引用和“覆盖此行为”的部分仅与 MRC 下的块的内存管理行为的单独问题有关。在 MRC 下,没有像 ARC 中那样变量为 __strong__weak 的概念。当 MRC 中的块捕获对象指针类型的变量时,默认情况下将其捕获为强引用(块保留它,并在块被释放时释放它),因为这是大多数期望的行为时间。如果您希望块不保留捕获的变量,唯一的方法是使其成为__block,这不仅使变量在两个作用域之间共享,而且使块不保留变量。所以这两个概念在 MRC 中被混为一谈了。

    在 ARC 中,一个块是强捕获还是弱捕获变量取决于捕获的变量是__strong 还是__weak(或__unsafe_unretained),并且与变量是否为__block 完全正交。如果需要,您可以拥有__block 的对象指针变量而不被弱捕获,或者弱捕获而不是__block,或者同时具有弱捕获和__block

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-20
      • 2019-10-22
      • 2015-04-11
      • 1970-01-01
      • 2014-08-31
      相关资源
      最近更新 更多