我应该在对象指针上使用__block说明符,即使它没有它吗?

tro*_*foe 5 variables macos block objective-c

我正在使用CAAnimation完成块(使用CAAnimationBlocks)在动画结束时提供处理,并且该完成块的一部分修改动画CALayer.如果对象指针保持不变,即使layer未使用说明__block符声明,这也可以工作,但我确实将对象视为读/写.

困扰我的Apple指南的一个方面是:

__block变量存在于变量的词法范围与在变量的词法范围内声明或创建的所有块和块副本之间共享的存储中.

鉴于它layer是一个集合迭代器,在我看来,如果我使用说明__block符,它实际上会破坏.

这是有问题的代码:

for (CALayer *layer in _myLayers)   // _myLayers is an ivar of the containing object
{
    CAAnimationGroup *group = ...;
    ...
    group.completion = ^(BOOL finished)
    {
        [CATransaction begin];
        [CATransaction setValue:(id)kCFBooleanTrue
                         forKey:kCATransactionDisableActions];
        layer.position = [self _findPosition];
        [CATransaction commit];

        [layer removeAnimationForKey:@"teleportEffect"];
    };

    [layer addAnimation:group forKey:@"teleportEffect"];
}
Run Code Online (Sandbox Code Playgroud)

我的实际问题是:我做得对吗(我的蜘蛛感觉刺痛).

编辑我还应该补充一点,我的应用程序使用MRR,但是保留/释放没有问题,因为这些层本质上是静态的(它们的生命周期是包含的NSView).此外,我似乎正在准确地做指导中避免模​​式的部分说我不应该这样做,尽管(对我而言)不清楚为什么.

ser*_*gio 1

编辑:

\n\n

至于您对提到的反模式的担忧,我认为在两个反模式示例中,关键点是变量声明和分配给它的“块文字”具有不同的范围。以for那里提出的案例为例:

\n\n
void dontDoThis() {\n  void (^blockArray[3])(void);  // an array of 3 block references\n\n  for (int i = 0; i < 3; ++i) {\n    blockArray[i] = ^{ printf("hello, %d\\n", i); };\n    // WRONG: The block literal scope is the "for" loop\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n
    \n
  1. blockArray在整个方法体内可见;

  2. \n
  3. 在里面for循环中,您创建一个块;块是一个对象(内存中的某些存储)并且有一个地址;作为对象的块具有“堆栈局部数据结构”(来自上面的参考),即,当您进入方法时,它在堆栈上分配;

  4. \n
  5. 事实上,“块文字”被视为循环的本地变量for,这意味着该存储可以在每次连续迭代中重用;

  6. \n
  7. 块地址被分配给blockArray元素;

  8. \n
  9. 当您退出for循环时,blockArray将包含可能不再存在和/或在每个步骤中被覆盖的块的地址,具体取决于编译器对在范围内创建的堆栈数据结构所做的操作for

  10. \n
\n\n

您的情况有所不同,因为您的局部变量也在作用域内for,并且在其外部不可见。

\n\n

作为反模式呈现的案例与此类似:

\n\n
 {\n int array[3];\n\n for (int i = 0; i < 3; ++i) {\n    int a = i;\n    array[i] = &a;\n    // WRONG: The block literal scope is the "for" loop\n }\n
Run Code Online (Sandbox Code Playgroud)\n\n

很可能,作用a域内的变量for只会在堆栈上分配一次,然后在循环的每次迭代中重用。原则上,a(一份副本)仍将存在于循环之外(我不确定,实际上,应该检查 C 标准),但很明显该代码的含义并不真正有意义。

\n\n

旧答案:

\n\n
\n

__block 变量存在于变量的词法作用域和变量\xe2\x80\x99s 词法作用域内声明或创建的所有块和块副本之间共享的存储中。

\n
\n\n

我认为可以这样更好地理解:__block变量的词法范围和所有块(根据上面的定义)将共享该变量的相同存储。因此,如果一个块(或原始词法作用域)修改了变量(我在这里指的是指向对象的变量),则该更改将对所有其他块可见。

\n\n

鉴于此,将变量声明为 as 的一个效果__block是,在非 ARC 情况下,它指向的对象不会传入它的每个块自动保留(使用 ARC,也对__block变量进行保留) 。

\n\n

无论使用 ARC 还是不使用 ARC,__block当您想要更改变量值并希望所有块都使用新值时,都需要使用说明符。想象一下,您有一个块来初始化_myLayersivar:在这种情况下,您需要将_myLayers变量作为变量传递到块中__block,以便可以修改它(相对于它的副本)。

\n\n

在你的情况下,如果你没有使用ARC,那么,这完全取决于layer执行块时所指向的对象是否仍然存在。由于layer来自_myLayers,这会转换为对象是否拥有_myLayers仍然存在。答案通常是肯定的,因为我们正在讨论的块是该层上动画的完成块。(比如说,如果它是网络请求的完成块,情况就会有所不同)。

\n\n

希望这可以帮助。

\n

  • 好的。但请注意,正如我所说,只有在使用“[block copy]”或“Block_copy”实际复制块后,才会保留非__block对象引用变量。不太确定 ARC 是如何工作的。当我自己做了一些实验而不调用 copy 时,它没有触发任何保留消息。 (3认同)
  • @sergio不确定是否误解了你的答案,但我很确定当编译为非ARC时`__block`指定变量是可变的并且它应该被视为指针而不是对象引用,这具有以下效果:如果复制块,则不会保留/释放该对象。如果块没有被复制,即使对象是“auto”(默认)或“__block”,也根本不会保留/释放。这就是我的测试所显示的以及我如何理解 [LLVM 块规范](http://clang.llvm.org/docs/BlockLanguageSpec.txt) (2认同)