数组中创建Block时,为何索引0的Block无需拷贝?代码与文档存疑
疑问:数组中创建的Block为何无需拷贝也能正常运行且不崩溃?
问题代码
id getBlockArr() { NSInteger val = 10; return [[NSArray alloc] initWithObjects:^{ NSLog(@"value: %ld", val);},^{ NSLog(@"value: %ld", val);},^{ NSLog(@"value: %ld", val);}, nil]; } int main(int argc, const char * argv[]) { @autoreleasepool { id arr = getBlockArr(); void(^blk)(void) = [arr objectAtIndex:0]; blk(); } return 0; }
问题描述
根据苹果官方文档,当上述代码中的blk被释放时程序会崩溃,但实际运行后控制台输出了value: 10,程序未出现崩溃情况。特此请教:在数组中创建Block时,为何索引为0的Block不需要拷贝?
回答
嘿,这个问题其实是ARC和MRC环境下Block行为差异导致的,我来给你拆解清楚:
首先得回忆下Block的内存分类:默认情况下,定义在函数内部的Block是栈Block(NSStackBlock),它的内存分配在当前函数的栈帧上。当函数执行完毕栈帧销毁后,栈Block的内存会被回收,这时候再去调用它就会触发崩溃——这也是苹果文档里提到的崩溃场景,但那是针对MRC手动引用计数环境的。
而你现在的代码是在ARC环境下运行的,编译器已经悄悄帮你做了关键处理:
- 当你把栈Block放入
NSArray这类容器时,ARC会自动调用copy方法,把栈Block拷贝到堆上,变成堆Block(NSMallocBlock)。堆Block的内存由ARC管理,只有在没有强引用时才会被释放,所以getBlockArr函数返回后,数组里的Block依然存活。 - 你可以做个小验证:在
main里加一行NSLog(@"Block class: %@", [blk class]);,输出会是__NSMallocBlock__,这就实锤了Block已经被自动拷贝到堆上了。
另外补充几个ARC下会自动触发Block拷贝的常见场景:
- 将Block赋值给强引用的属性
- 将Block作为函数返回值返回
- 将Block放入
NSArray、NSDictionary等容器类中
所以不是这个Block不需要拷贝,而是ARC已经在背后帮你完成了拷贝操作,你不用手动调用[blk copy],自然也就不会出现崩溃啦。
内容的提问来源于stack exchange,提问作者GodCard
相关产品推荐
相关产品推荐

