为什么必须复制NSBlocks才能存储在容器中? [英] Why do NSBlocks have to be copied for storage in containers?

查看:114
本文介绍了为什么必须复制NSBlocks才能存储在容器中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

- (void) addABlock 
{
void (^aBlock)(void) = ^() { [someObject doSomething]; };

[self.myMutableArray addObject: aBlock];  // Oops..

[self.myMutableArray addObject: [aBlock copy]];  // works fine
}

在上面的简化示例中,如果未执行块复制,则会看到未定义的行为.苹果的ARC过渡指南中专门列出了这种情况.

In the above simplified example I am seeing undefined behavior if the block copy is not performed. This case is specifically listed in apple's ARC transition guide.

我不理解的部分是为什么,我必须手动调用副本.该块是在堆栈上创建的,因此需要执行block_copy -这很清楚. NSArray不调用复制,但应在添加的对象上调用保留.那么,为什么[NSBlock保留]不能简单地调用[NSBlock复制]?

The part that I do not understand is why I have to manually call copy. The block is created on the stack so a block_copy needs to be performed- that much is clear. NSArray does not call copy, but it should call retain on objects that get added. So why doesn't [NSBlock retain] simply call through to [NSBlock copy] ?

http://developer.apple .com/library/mac/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html

http://www.galloway.me.uk/2013/05/a-look-inside-blocks-episode-3-block-copy/

推荐答案

更新

尽管 Apple文档怎么说:

在ARC模式下(例如在返回中)向上传递堆栈时,阻止正常工作".您无需再致电阻止复制".将堆栈向下"传递到arrayWithObjects:以及执行保留的其他方法时,仍然需要使用[^{} copy].

Blocks "just work" when you pass blocks up the stack in ARC mode, such as in a return. You don’t have to call Block Copy any more. You still need to use [^{} copy] when passing "down" the stack into arrayWithObjects: and other methods that do a retain.

将其添加到容器中时,不再需要 在块上手动调用copy. 在这种情况下,缺少自动复制已被认为是编译器错误,并且很早以前已在llvm中修复.

it's no longer necessary to manually call copy on a block when adding it to a container. The lack of automatic copy in this case has been considered a compiler bug and fixed in llvm long time ago.

我们认为这是一个编译器错误,并且在开源clang存储库中已修复了几个月."

"We consider this to be a compiler bug, and it has been fixed for months in the open-source clang repository."

( John McCall ,LLVM开发人员)

(John McCall, LLVM developer)

我使用最新的Apple LLVM 5.0编译器在Xcode 5中对此进行了亲自测试.

I personally tested this in Xcode 5, using the latest Apple LLVM 5.0 compiler.

- (NSArray *)blocks {
    NSMutableArray *array = [NSMutableArray array];
    for (NSInteger i = 0; i < 3; i++) {
        [array addObject:^{ return i; }];
    }
    return array;
}

- (void)test {
    for (NSInteger (^block)() in [self blocks]) {
        NSLog(@"%li", block());
    }
}

上面的示例正确打印

0
1
2

在ARC下运行,并在MRC中以EXC_BAD_ACCESS崩溃.

under ARC and it crashes with EXC_BAD_ACCESS in MRC.

请注意,这最终-与llvm 文档保持一致

Note that this is - finally - coherent with the llvm documentation, which states

只要这些语义要求保留块指针类型的值,它就会产生Block_copy

意味着只要ARC必须保留一个指针并且该指针恰好是块指针类型,就会调用Block_copy而不是retain.

meaning that whenever ARC has to retain a pointer and this pointer happens to be a block-pointer type, Block_copy will be called instead of retain.

我不理解的部分是为什么我必须手动调用copy.

The part that I do not understand is why I have to manually call copy.

块是在堆栈上分配的Objective-C对象的少数示例之一(出于性能原因),因此,由于当前堆栈帧的中断,当您从方法调用返回时,它们会丢失.

Blocks are one of the few examples of Objective-C objects allocated on the stack (for performance reasons), so when you return from the method call you lose them, due to the tear down of the current stack frame.

在堆栈块上发送copy将在其上调用Block_copy并将其在堆上移动,从而使您可以保留对该块的有效引用.

Sending copy on a stack-block will call Block_copy on it and it will move it on the heap, allowing you to keep a valid reference to the block.

那么[NSBlock retain]为什么不简单地拨打[NSBlock copy]

So why doesn't [NSBlock retain] simply call through to [NSBlock copy]

这将破坏retain的通常语义,该语义应该返回具有增加的保留计数的对象本身.由于增加堆栈块上的保留计数没有任何意义,因此在堆栈块上调用retain没有任何作用.

This would break the usual semantic of retain, which is supposed to return the object itself, with an incremented retain count. Since incrementing a retain count on an stack-block doesn't make any sense, calling retain on a stack-block doesn't have any effect.

Apple可以按照您的建议以不同的方式实现它,但他们宁愿尽可能地遵循内存管理方法的通用协定.

Apple could have implemented it differently, as you suggest, but they preferred to stick as much as possible to the common contracts of memory management methods.

作为有关块的进一步参考,您可能需要看看此@bbum撰写的精彩博文.它是ARC之前的版本,但是大多数概念都没有改变.

As a further reference on blocks, you may want to have a look at this great blog post by @bbum. It's pre-ARC but the majority of concepts hasn't changed.

这篇关于为什么必须复制NSBlocks才能存储在容器中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆