【问题标题】:Storing Blocks in an Array将块存储在数组中
【发布时间】:2011-12-21 07:28:24
【问题描述】:

在 Objective-C 中,我知道块被视为对象,所以我想知道是否可以将它们存储在数组中。这就引出了一个问题,块是头等对象还是只是为了在对象之间传递它们而将它们视为对象?如果它们是一流的对象,那么它们不应该存储在数组中吗?

【问题讨论】:

    标签: objective-c memory-management objective-c-blocks


    【解决方案1】:

    编辑:无需过多介绍,在 ARC 下,您现在可以像任何其他对象一样将块添加到集合中(参见讨论)。

    我在下面保留了原始答案,因为它包含一些有趣的技术细节。


    这引出了一个问题,是块头等对象还是它们 只是为了在它们之间传递而将它们视为对象 对象?如果它们是一流的对象,那么它们不应该是 可以存储在数组中吗?

    块是 Objective-C 对象,其行为与其他 NSObject 非常相似,但有几个关键区别:

    • 块始终由编译器生成。当执行通过块声明时,它们在运行时有效地“分配/初始化”。

    • 最初在堆栈上创建块。如果 Block 要超过当前范围,则必须使用 Block_copy() 或 copy 方法将 Block 移动到堆中(请参阅下面的 ARC 点)。

    • 除了内存管理之外,块实际上没有可调用的 API。

    • 要将块放入集合中,必须首先复制它总是。包括在 ARC 下。 (参见 cmets。) 如果不这样做,则存在分配的堆栈 Block 为 autoreleased 的风险,并且您的应用稍后会崩溃。

    • 复制基于堆栈的块也会复制所有捕获的状态。如果您要制作一个块的多个副本,则先复制一次,然后再复制副本更有效(因为复制副本只会增加保留计数,因为块是不可变的)。

    • 在 ARC 下,从方法或函数返回块“正常工作”;它会被自动复制到堆中,并且返回实际上是一个自动释放的块(编译器可能会在某些情况下优化掉自动释放)。即使使用 ARC,您仍然需要先复制块,然后再将其粘贴到集合中。

    我写了几篇博文,都提供了introduction to blocks 和一些tips and tricks。你可能会觉得它们很有趣。

    而且,是的,将它们添加到字典中非常有用。我编写了一些代码,将块作为命令处理程序放入字典中,其中键是命令名称。非常方便。

    【讨论】:

    • 在 ARC 下,添加到集合时不再需要复制块! Apple 在他们的“ARC 过渡指南”中表示,但该指南已经过时了。请查看blog.parse.com/2013/02/05/objective-c-blocks-quiz - 特别是示例 B 和示例 C 的答案,以及最后的“结论”。即使在 ARC 下也需要复制块,这源于 clang 中的编译器错误(很久以前就已修复),并且只是一种解决方法,因为 Apple 不能等待这个错误在发布下一个 Xcode 版本之前首先得到修复。
    • 谢谢@Mecki。我需要回去编辑我所有关于块的答案。 :)
    • 我刚刚在 Xcode 5 (Apple LLVM 5.0) 提供的最新 llvm 版本上测试了 Mecki 所说的内容。该错误不再存在,因此块可以安全地存储在容器中,而无需在 ARC 下显式复制。
    • @GabrielePetronella:没有多少测试可以证明 C 代码是正确的,因为它可能是未定义的行为。
    • @GabrielePetronella:但是 ARC 规范中没有任何地方说直接作为参数传递给 addObject:(例如)的块文字被复制。
    【解决方案2】:

    是的,块确实是对象,你可以把它们放在数组中:

    NSMutableArray *arr = [NSMutableArray new];
    [arr addObject:^(){NSLog(@"my block");}];
    void (^ myblock)() = [arr objectAtIndex:0];
    myblock();
    

    这会将“我的块”放在控制台中。

    【讨论】:

    • 块在被添加到集合之前必须被复制。总是。即使在 ARC 下。
    • @bbum:块不必再在 ARC 下复制,即使添加到集合中也是如此。 Apple 在他们的过渡指南中这么说,但这只是为了解决一个很久以前就修复的 clang 编译器错误。请参阅blog.parse.com/2013/02/05/objective-c-blocks-quiz - 特别是对 Example BExample C 和末尾的 Conclusion 的回答(也链接到 LLVM 的帖子维护人员解释情况)。
    • @Mecki:这是完全错误的。在 C 中,仅仅因为某些东西没有崩溃并不意味着它是正确的——它可能是未定义的行为。 ARC 规范中的任何内容都不能保证在这种情况下会复制块。仅仅因为这个版本的 ARC 编译器恰好插入了一个副本,并不意味着你可以依赖它。
    • @newacct 如果 LLVM 开发人员说必须复制数组的事实是一个错误,并且在这种情况下 ARC 的正确行为是为您复制数组,那么就没有什么可做的了争论。 LLVM 有自己的 ARC 规范,除了 LLVM 开发人员之外,没有人决定在 clang 中什么是正确的 ARC 行为,什么不是。 Apple 可以声称相反,但事实是 Apple 没有自己的编译器,他们使用 clang 和 clang 的工作方式是 LLVM.org 希望它工作的方式,而不是 Apple 希望它工作的方式(Apple 没有自己的 clang 分支)。
    • @newacct 仅供记录(所有内容都已存档)以及将来遇到此问题的每个人:John McCall 说您在 ARC 中不需要 Block_copy。见bit.ly/1br5J7Z 如果你不知道约翰·麦考尔是谁,见bit.ly/19gnnGD 并搜索他的名字。我相信 John McCall 比任何 SO 用户告诉你的要多一百万倍。启用 ARC 后,编译器应该始终知道何时必须将块复制到堆中(在需要时没有这样做将是一个错误),无需调用 Block_copy;它甚至可能会破坏编译器优化。
    猜你喜欢
    • 1970-01-01
    • 2014-12-04
    • 1970-01-01
    • 2015-12-23
    • 2012-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多