You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

数组中创建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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:19:02