关于`partial apply for closure #1 in closure #1 in Double.init<A>(_:)`崩溃的技术问询
崩溃原因分析与解决方案
可能的崩溃原因
从崩溃栈的EXC_BAD_ACCESS KERN_INVALID_ADDRESS错误、Swift引用计数释放流程,以及Block释放环节来看,核心问题是野指针访问,具体指向以下几种场景:
- 闭包捕获对象提前释放:
Double.init<A>(_:)的嵌套闭包中捕获的对象被提前释放,当闭包自身被ARC回收时,尝试访问已失效的引用计数导致崩溃。 - Swift-OC混编的Block内存漏洞:OC Block捕获Swift对象时未用
weak/unowned修饰,导致Block持有已被Swift ARC释放的对象,释放Block时触发野指针访问。 - 自定义Double初始化的闭包逻辑缺陷:版本更新新增的Double扩展/自定义初始化里,闭包存在强制解包、未处理弱引用nil值等问题,引发内存访问异常。
触发崩溃的代码示例
场景1:Swift闭包捕获对象提前释放
class DataHolder { var rawValue: String = "123.45" deinit { print("DataHolder deinit") } } extension Double { // 自定义初始化方法,接收返回LosslessStringConvertible类型的闭包 init<A>(unsafeClosure: () -> A) where A: LosslessStringConvertible { let stringValue = String(unsafeClosure()) self.init(stringValue) ?? 0.0 // 闭包被隐式保留,后续释放时会访问捕获的对象 } } func triggerCrash() { var holder: DataHolder? = DataHolder() // 闭包捕获holder的强引用 let _ = Double(unsafeClosure: { holder!.rawValue }) // 提前释放holder对象 holder = nil // 当Double实例被回收时,闭包释放,尝试访问已释放的holder的引用计数,触发崩溃 } triggerCrash()
场景2:OC Block捕获Swift对象未处理弱引用
// OC文件:OCBlockManager.h #import <Foundation/Foundation.h> typedef void(^CalculationBlock)(void); @interface OCBlockManager : NSObject @property (nonatomic, copy) CalculationBlock calcBlock; - (void)attachBlockWithSwiftObject:(id)obj; @end // OC文件:OCBlockManager.m @implementation OCBlockManager - (void)attachBlockWithSwiftObject:(id)obj { self.calcBlock = ^{ // Block强引用obj,若obj是Swift对象被ARC释放后,此处访问会崩溃 NSLog(@"Obj value: %@", obj); }; } @end
// Swift文件 class SwiftData: NSObject { var numberString: String = "67.89" deinit { print("SwiftData deinit") } } func triggerOCBlockCrash() { let manager = OCBlockManager() var swiftData: SwiftData? = SwiftData() // Block捕获swiftData的强引用 manager.attachBlockWithSwiftObject(swiftData!) // 释放Swift对象 swiftData = nil // 当manager的calcBlock被释放时,访问已回收的swiftData,触发EXC_BAD_ACCESS } triggerOCBlockCrash()
排查与修复建议
- 定位代码变更:重点检查版本更新中
Double扩展、自定义初始化方法,以及Swift-OC混编新增的Block相关逻辑。 - 启用Zombie Objects:在Xcode中开启
Edit Scheme > Run > Diagnostics > Zombie Objects,运行App重现崩溃,可直接定位到被释放后仍被访问的对象。 - 修复闭包/Block捕获逻辑:对闭包/Block中捕获的对象使用
weak/unowned修饰,避免强引用循环或野指针访问,例如:let _ = Double(unsafeClosure: { [weak holder] in holder?.rawValue ?? "0.0" }) - 避免强制解包:将闭包中的强制解包改为可选绑定,防止nil值访问引发的内存异常。
内容的提问来源于stack exchange,提问作者Matteo Gobbi
相关产品推荐
相关产品推荐

