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

时间:2012-09-16 08:31:25

标签: objective-c macos variables block

我使用CAAnimation完成块(使用CAAnimationBlocks)在动画结束时提供处理,部分完成块修改动画CALayer。即使layer未使用__block说明符声明,但是在对象指针保持不变的情况下,这仍然有效,但我确实将对象视为读/写。

困扰我的Apple Guide的一个方面是:

  

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

鉴于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"];
}

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

编辑我还应该补充一点,我的应用程序使用MRR,但是由于图层在本质上是 static ,因此保留/释放没有问题(它们的生命周期是包含NSView)。此外,我似乎正在做{{3>}的{em>要避免的模式部分说我不应该做的事情,尽管我不清楚(对我而言)为什么。

2 个答案:

答案 0 :(得分:4)

__block变量在块(以及封闭范围)内是可变的,并且如果将任何引用块复制到堆中,则会保留这些变量。

我不认为在你的情况下你需要一个块变量,因为你正在更改块内的对象layer的值,因为它属于似乎是_myLayers的{​​{1}}数组一个实例变量很难在每个块执行之前释放对象...但是你可以添加__block存储类型修饰符来保留对象,但是如果使用 ARC < / strong>,在复制块并稍后释放时,对象变量会自动保留和释放

答案 1 :(得分:1)

编辑:

至于你对你提到的Anti-patterns的关注,我认为在两个反模式的例子中,关键点是变量声明,分配给它的“块文字”具有不同的范围。采取那里提出的for案例:

void dontDoThis() {
  void (^blockArray[3])(void);  // an array of 3 block references

  for (int i = 0; i < 3; ++i) {
    blockArray[i] = ^{ printf("hello, %d\n", i); };
    // WRONG: The block literal scope is the "for" loop
  }
}
  1. blockArray在整个方法体中可见;

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

  3. “块文字”被视为for循环的局部变量,这意味着该存储可以在每次连续迭代中重复使用;

  4. 将块地址分配给blockArray个元素;

  5. 退出for循环时,blockArray将包含可能不再存在的块的地址和/或在每一步都被覆盖,具体取决于编译器要执行的操作在for范围内创建的数据结构。

  6. 您的情况不同,因为您的本地变量也在for范围内,并且在其外部不可见。

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

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

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

    老答案:

      

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

    我认为可以更好地理解这一点:__block变量的词法范围和所有块(按照上面的定义)将共享该变量的相同存储。因此,如果一个块(或原始词法范围)修改变量(我的意思是指向该对象的变量),那么所有其他变量都可以看到该变化。

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

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

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

    希望这有帮助。