在Objective-C++中存储Objective-C Block到std::function是否安全?
这是个非常关键的细节——哪怕你的小示例跑起来没崩溃,直接把栈Block赋值给std::function的操作本质上是不安全的,我来给你拆解清楚背后的逻辑和正确的处理方式:
1. 栈Block的核心风险
Block默认分配在栈内存上,当它所在的作用域(比如某个函数、局部代码块)执行完毕后,栈上的这块内存会被系统回收或复用。如果你的std::function在这个作用域之外还持有这个栈Block的引用,后续调用std::function时就会访问已经失效的内存,大概率触发崩溃或者不可预测的行为。
你当前的小示例能正常运行,只是因为作用域还没结束,栈内存还没被覆盖,属于“碰巧没出问题”的情况,不能代表操作安全。
2. ARC在这里的处理逻辑
ARC只会自动管理Objective-C对象的生命周期,但栈Block并不是Objective-C对象——只有被复制到堆上的Block才会成为ARC可管理的对象。ARC不会自动感知到你把栈Block赋值给std::function的操作,所以不会主动帮你把栈Block复制到堆上。
换句话说,std::function只是单纯保存了Block的指针,ARC不会为这个操作触发栈转堆的复制逻辑。
3. 安全的正确做法
要确保安全,你需要显式把栈Block复制到堆上,再赋值给std::function,具体有两种常用写法:
- 方式一:使用
Block_copy()C函数(对应释放用Block_release(),不过ARC会自动管理堆Block的引用计数,无需手动调用):// 定义一个栈Block void(^stackBlock)(NSString*) = ^(NSString* msg) { NSLog(@"Received: %@", msg); }; // 显式复制到堆后赋值给std::function std::function<void(NSString*)> safeFunc = Block_copy(stackBlock); - 方式二:直接用Objective-C的
copy方法,写法更简洁:std::function<void(NSString*)> safeFunc = [^(NSString* msg) { NSLog(@"Received: %@", msg); } copy];
额外补充:如果你的Block是定义在全局作用域或者静态变量中的,它本身就是全局Block(既不在栈也不在堆),这种情况直接赋值给std::function是安全的,但绝大多数业务场景里的Block都是栈Block,所以显式复制是通用的安全准则。
内容的提问来源于stack exchange,提问作者fabe

