Cocoa阻止作为强大的指针与副本

时间:2014-11-26 15:12:25

标签: objective-c cocoa automatic-ref-counting objective-c-blocks

我用积木做了好几次,就像我有强烈参考的指针一样

我听说你应该使用copy,但使用块作为指针而不是原始对象有什么意义?

我从来没有得到编译器的抱怨,我不应该使用

@property (nonatomic, strong) MyBlock block;

但应该使用

@property (nonatomic, copy) MyBlock block;

据我所知,该块只是一个对象,所以为什么还要更喜欢复制呢?

5 个答案:

答案 0 :(得分:47)

简答

答案是历史性的,你完全正确的是,在当前的ARC代码中,没有必要使用copystrong属性。例如,本地变量和全局变量也是如此。

长答案

与其他对象不同,块可以存储在堆栈中,这是实现优化,因此应该像其他编译器优化一样,不会对其产生直接影响书面代码。这种优化有一个常见的情况,即创建一个块,作为方法/函数参数传递,由该函数使用,然后丢弃 - 该块可以快速分配到堆栈上,然后在没有堆的情况下处理(动态内存池)参与。

将此与局部变量进行比较,其中(a)在堆栈上创建,(b)在拥有函数/方法返回时自动销毁,(c)可以被地址传递给拥有函数调用的方法/函数。在其拥有的函数/方法返回后,无法存储和使用局部变量的地址 - 该变量不再存在。

但是对象应该比它们的创建函数/方法(如果需要)更长,因此与局部变量不同,它们在上分配,并且不会基于它自动销毁他们的创建功能/方法返回,而是基于他们是否仍然需要 - 以及"需要"这几天是由ARC自动确定的。

在堆栈上创建一个块可能会优化一个常见的情况,但它也会导致一个问题 - 如果块需要像对象那样经常使用它的创建者,那么它必须在它的创建者堆栈被销毁之前移动到堆中。

当块实现首次发布时,程序员可以看到堆栈上存储块的优化,因为当时编译器无法在需要时自动处理将块移动到堆 - 程序员必须使用函数{ {1}}自己做。

虽然这种方法在低级C世界中可能不是不合适的(并且块是C构造),但是让高级Objective-C程序员手动管理编译器优化实际上并不好。随着Apple发布了更新版本的编译器改进。早期,程序员被告知他们可以用block_copy()替换block_copy(block),并使用普通的Objective-C对象。然后编译器开始根据需要自动从块中复制块,但这并不总是正式记录。

虽然Apple不能摆脱其起源并且指的是这样做并且最好的做法" - 这当然值得商榷。在Apple的Working with Blocks最新版本2014年9月,他们表示块值属性应该使用[block copy],但随后会立即变得干净(强调添加):

  

注意:您应该将copy指定为属性属性,因为需要复制块以跟踪其在原始范围之外的捕获状态。 这不是您在使用自动引用计数时需要担心的事情,因为它会自动发生,但属性属性的最佳做法是显示结果行为。

没有必要"显示由此产生的行为" - 首先将块存储在堆栈中的是优化,并且对代码应该是透明的 - 就像其他编译器优化一样,代码应该在没有程序员参与的情况下获得性能优势。

因此,只要您使用ARC和当前的Clang编译器,您就可以像其他对象一样处理块,并且因为块是不可变的,这意味着您不需要复制它们。相信苹果公司,即使他们似乎怀念我们过去手工做的好时光。并鼓励您在代码中保留历史提醒,不需要copy

你的直觉是正确的。

HTH

答案 1 :(得分:3)

您询问的是属性的所有权修饰符。如果合成(或自动合成),这会影响合成(或自动合成)属性的吸气剂和/或设定器。

这个问题的答案在MRC和ARC之间会有所不同。

  • 在MRC中,属性所有权修饰符包括assignretaincopy。 ARC引入了strong,当在MRC中使用strong时,它与retain同义。所以问题是关于retaincopy之间的区别,并且存在很多差异,因为copy的setter会保存给定值的副本。< / p>

    需要复制块以在创建它的范围之外使用(使用块文字)。由于您的属性将值存储为跨函数调用持久存在的实例变量,并且有人可能会从创建它的作用域中分配未占用的块,因此惯例是您必须复制它。 copy是正确的所有权修饰符。

  • 在ARC中,strong使基础实例变量__strongcopy也使其成为__strong,并将复制语义添加到setter。但是,ARC还保证只要将值保存到块指针类型的__strong变量中,就会完成复制。您的属性具有类型MyBlock,我假设它是块指针类型的typedef。因此,如果所有权限定符为strong,则仍会在setter中完成复制。因此,在ARC中,对此属性使用strongcopy之间没有区别。

如果此声明可能同时用于MRC和ARC(例如库中的标题),最好使用copy以便在两种情况下都能正常工作。

答案 2 :(得分:2)

what is the implication in working with blocks as pointers and not with the raw object?

你永远不会使用原始值,你总是有一个指向块的指针:一个块是一个对象。

看一下你的具体例子,我假设你想要保留这个块,&#34;那么为什么要优先选择复制&#34; enter code here?嗯,这是安全的问题(这个例子来自Mike Ash博客)。由于块是在堆栈上分配的(而不是像在objective-c中的其余对象那样在堆上),所以当你做这样的事情时:

[dictionary setObject: ^{ printf("hey hey\n"); } forKey: key];

您正在当前作用域的堆栈帧上分配块,因此当作用域结束时(例如返回字典),堆栈帧将被销毁,并且块将随之移动。所以你有一个悬垂的指针。我建议完全阅读Mike's article。无论如何,如果您在分配块时复制它,可以使用strong属性:

self.block = [^{} copy];

编辑:在查看Mike的文章日期之后,我假设这是Pre-ARC的行为。在ARC看来,它似乎按预期工作,并且它不会崩溃。

编辑2:在尝试使用非ARC之后,它也不会崩溃。但是这个例子显示了不同的结果,具体取决于ARC的使用:

void (^block[10])();

int i = -1;
while(++i < 10)
    block[i] = ^{ printf("%d\n", i); };


for(i = 0; i < 10; i++)
    block[i]();

引用Mike Ashe的不同结果:

  

在第一种情况下打印10个9s的原因非常简单:   在循环中创建的块具有与之相关的生命周期   循环的内部范围。该块在下一次迭代时被销毁   循环,以及离开循环时。当然,&#34;销毁&#34;只是意味着   堆栈上的插槽可以覆盖。它只是   发生这种情况,编译器每次重复使用相同的插槽   循环,所以最后,数组填充相同的指针,和   因此你会得到相同的行为。

答案 3 :(得分:0)

  

注意:您应该将copy指定为属性属性,因为需要复制块以跟踪其在原始范围之外的捕获状态。在使用自动引用计数时,您不必担心这一点,因为它会自动发生,但属性属性的最佳做法是显示结果行为。有关详细信息,请参阅Blocks Programming Topics

答案 4 :(得分:-1)

据我所知,当对象可变时需要copy。如果您此时需要对象的值,请使用此选项,并且您不希望该值反映对象的其他所有者所做的任何更改。完成后,您需要释放该对象,因为您要保留该副本。

另一方面,strong表示您拥有该对象,直到需要它为止。它是retain属性的替代,是ARC的一部分。

来源:Objective-C declared @property attributes (nonatomic, copy, strong, weak)