我使用objective-c块编写了一些代码,但结果让我很困惑。
@interface MyTest : NSObject
@end
@implementation MyTest
- (void)test {
NSArray *array = [self array1]; // ok
// NSArray *array = [self array2];// crash
// NSArray *array = [self array3];// ok again
dispatch_block_t block0 = (dispatch_block_t)[array objectAtIndex:0];
block0();
dispatch_block_t block1 = (dispatch_block_t)[array objectAtIndex:1];
block1();
}
- (NSArray *)array1 {
int a = 10;
NSMutableArray *array = [NSMutableArray array];
[array addObject:^{
NSLog(@"block0: a is %d", a);
}];
[array addObject:^{
NSLog(@"block1: a is %d", a);
}];
return array;
}
- (NSArray *)array2 {
int a = 10;
return [NSArray arrayWithObjects:^{
NSLog(@"block0: a is %d", a);
}, ^{
NSLog(@"block1: a is %d", a);
}, nil];
}
- (NSArray *)array3 {
int a = 10;
return [NSArray arrayWithObjects:^{
NSLog(@"block0: a is %d", a);
},[^{
NSLog(@"block0: a is %d", a);
} copy], nil];
}
@end
我很困惑:
BTW,我在ARC下的Xcode 4.6中运行代码。感谢
答案 0 :(得分:4)
您似乎找到了与编译器无法处理的块相关的类型丢失的情况。但我们需要从头开始......
以下内容涉及ARC下块的使用。不考虑其他场景(MRC,GC)。
在堆栈上创建了一些块而不是堆是优化,可以在技术上以这样的方式实现,即程序员永远不需要知道它。但是,当首次引入块时,决定优化对用户不透明,因此引入blockCopy()
。从那时起,规范和编译器都已经发展(并且编译器实际上超出了规范),并且blockCopy()
不是(通过规范)需要它放置它, 可能(因为编译器可能超出规范)在其他人中需要。
如何透明地实施优化?
考虑:
琐碎的回答是“是” - 在任何任务上移动到堆上。但这会否定优化的整个目的 - 创建一个堆栈块,将其传递给另一个方法,该方法涉及并赋值给参数......
简单的答案是“不要尝试” - 介绍blockCopy()
并让程序员弄明白。
更好的答案是“是” - 但要巧妙地做到这一点。在伪代码中,案例是:
// stack allocated block in "a", consider assignment "b = a"
if ( b has a longer lifetime than a )
{
// case 1: assigning "up" the stack, to a global, into the heap
// a will die before b so we need to copy
b = heap copy of a;
}
else
{
if (b has a block type)
{
// case 2: assigning "down" the stack - the raison d'être for this optimisation
// b has shorter life (nested) lifetime and is explicitly typed as a block so
// can accept a stack allocated block (which will in turn be handled by this
// algorithm when it is used)
b = a;
}
else
{
// case 3: type loss - e.g. b has type id
// as the fact that the value is a block is being lost (in a static sense)
// the block must be moved to the heap
b = heap copy of a;
}
}
在介绍块案例1& 3要求手动插入blockCopy()
,案例2是优化得到回报的地方。
然而,正如an earlier answer中的解释,规范现在涵盖了案例1,而编译器似乎涵盖了案例3,但没有文档确认已知。< / p>
(顺便说一句,如果您按照该链接进行操作,您会看到它包含有关此主题的older question的链接。现在描述的案例现已自动处理,这是上述案例1的示例。)
Phew,得到了所有这些?让我们回到问题中的例子:array1
,array3
和array4
都是案例3中存在类型丢失的示例。它们也是前一个问题中测试的场景,并且发现由当前编译器处理。他们工作不是偶然或运气,编译器显式插入所需的块副本。但是我不知道这在任何地方都有正式记载。array2
也是案例3的一个示例,其中存在类型丢失,但它是在前一个问题中未测试的变体 - 通过作为变量参数列表的一部分传递来输入类型。这种情况 not 似乎是由当前编译器处理的。所以现在我们有一个线索,为什么没有记录案例3的处理 - 处理不完整。请注意,如前所述,可以测试编译器的功能 - 您甚至可以在代码中加入一些简单的测试,以便在测试失败时立即中止应用程序。所以你可以,如果你愿意,根据你所知道的编译器当前自动处理的代码编写代码(到目前为止,所有内容都被认为是接受可变参数函数),如果更新编译器和更换代码,将中止代码缺乏支持。
希望这有用并且有意义!
答案 1 :(得分:1)
所有这三个崩溃对我来说(虽然我怀疑copy
的第一个元素上缺少array3
可能是疏忽。)如果你想要它,必须复制一个块比创建它的范围更长。除非您特别知道方法复制了传递给它的对象,否则您需要自己复制它。
答案 2 :(得分:0)
我尝试了第四个也很好的案例:
- (NSArray *)array4 {
int a = 10;
return @[ ^{
NSLog(@"block0: a is %d", a);
}, ^{
NSLog(@"block1: a is %d", a);
}
];
}
当然这与:
相同- (NSArray *)array4 {
int a = 10;
id blocks[] = { ^{
NSLog(@"block0: a is %d", a);
}, ^{
NSLog(@"block1: a is %d", a);
}
};
NSUInteger count = sizeof(blocks) / sizeof(id);
return [NSArray arrayWithObjects:blocks count:count];
}
所以唯一的问题是“array2”。该实现的关键点是您正在调用arrayWithObject:
方法,该方法接受可变数量的参数。
似乎只有第一个(命名的)参数被正确复制。没有复制任何变量参数。如果添加第三个块,问题仍然出现在第二个块上。只复制第一个块。
所以似乎使用带有变量参数构造函数的块,实际上只复制了第一个命名参数。没有复制任何变量参数。
在创建数组的所有其他方法中,每个块都被复制。
BTW - 我使用ARC在Lion(10.7.5)下使用简单的OS X应用程序使用Xcode 4.6.2运行代码和我的添加。当在iOS 6.1应用程序中使用相同的代码时,我得到相同的结果。