如何在延迟执行的Block中持有传入参数的引用避免非法内存访问崩溃?
weak转strong实现的关键遗漏点
先纠正一个常见的认知偏差:你提到“不持有参数引用就会出现非法内存访问崩溃”,这更多是MRC手动管理内存时代的遗留体验——在ARC环境下,只要正确声明弱引用,对象释放后弱指针会自动置为nil,不会触发野指针崩溃。你之前遇到的崩溃,本质是Block捕获了没有被置空的悬空指针值,并不是说你必须强持有对象才能避免崩溃。
你目前写的weak转strong逻辑,通常容易遗漏这几个核心细节:
- 强引用变量的定义位置错误:很多人图省事把
__typeof(Foo) *strongFoo = weakFoo;写在Block外部的方法作用域里,这种写法完全达不到weak转strong的设计效果——Block本身会捕获外部的strongFoo变量的强引用,直接形成和原对象的强持有链,跟你直接在Block里用原foo变量没有区别,既可能引入循环引用,也达不到“对象释放时弱引用自动失效”的设计目标。strongFoo的定义必须放在Block内部。 - 缺少nil判空逻辑:从你声明weakFoo、到Block真正被调度执行的时间差里,foo很可能已经被释放,这时候weakFoo会返回nil。如果不对转出来的strongFoo做判空就直接执行业务逻辑,会出现逻辑静默失效——比如你本来要给foo发送网络回调的更新逻辑,结果因为strongFoo是nil,方法调用直接空跑,没有任何报错,线上问题极难排查。
- 没有匹配对应业务场景:如果你的业务要求Block执行时foo必须存在、否则这次Block的执行完全没有意义,那强行用weak转strong本身就是错误选型——你需要的是让Block持有foo的强引用,保证它在Block生命周期内不被释放,这时候用weak反而会导致关键逻辑执行失败。
符合规范的weak转strong写法参考:
- (void)doSomethingWithParamter:(Foo *)foo { __weak __typeof(Foo) weakFoo = foo; [Bar setCompletionHandler:^{ // 强引用转换必须放在Block内部 __typeof(Foo) strongFoo = weakFoo; // 必须做判空,处理对象已提前释放的场景 if (!strongFoo) { return; } // Block内所有对foo的操作统一用strongFoo,不要再直接操作weakFoo [strongFoo refreshState]; }]; }
补充说明:这种写法的核心作用是,在Block被执行的整个周期内,临时持有foo的强引用,保证执行过程中foo不会被突然释放;当Block执行完毕后,这个临时强引用会自动释放,既不会造成长期的内存持有,也不会因为跨多长链路的异步执行,导致中途对象被释放出现的逻辑断层。
内容的提问来源于stack exchange,提问作者Sakshi Malhotra
相关产品推荐
相关产品推荐

