Swift中已做Optional判断仍出现解包nil崩溃的排查求助
崩溃排查:Optional解包异常问题
问题描述
从Xcode>Window>Organizer>crashes收集到用户崩溃日志,日志显示代码第3行触发「Unexpectedly found nil while unwrapping an Optional value」错误,但该行已通过if let进行Optional值判断,且确认日志解析后的行号无误,需要排查建议。
相关代码片段
model.weekday = workout.startDate.dayOfWeek() model.shortDate = workout.startDate.toString(format: "MM/dd") if let totalEnergyBurned = workout.totalEnergyBurned { // Unexpectedly found nil while unwrapping model.kilocalorie = (Int)(round(totalEnergyBurned.doubleValue(for: HKUnit.kilocalorie()))) }
崩溃日志详情
Exception Type: EXC_BREAKPOINT (SIGTRAP) Exception Codes: 0x0000000000000001, 0x0000000100cdaaa4 Termination Signal: Trace/BPT trap: 5 Termination Reason: Namespace SIGNAL, Code 0x5 Terminating Process: exc handler [21903] Triggered by Thread: 17 Thread 17 name: Thread 17 Crashed: 0 DunDun 0x0000000100cdaaa4 Swift runtime failure: Unexpectedly found nil while unwrapping an Optional value + 0 (DDWorkout.swift:0) 1 DunDun 0x0000000100cdaaa4 specialized static DDWorkout.convert(_:) + 1936 (DDWorkout.swift:59) 2 DunDun 0x0000000100cda868 specialized static DDWorkout.convert(_:) + 1364 (DDWorkout.swift:59) 3 DunDun 0x0000000100cb0a90 static DDWorkout.convert(_:) + 8 (<compiler-generated>:0) 4 DunDun 0x0000000100cb0a90 completion #1 (_:error:) in HealthKitStorage.getTotalWorkout(callback:) + 752 5 DunDun 0x0000000100cb0ecc closure #1 in HealthKitStorage.getTotalWorkout(callback:) + 496 (HealthKitStorage.swift:55) 6 DunDun 0x0000000100cb1590 thunk for @escaping @callee_guaranteed (@guaranteed HKSampleQuery, @guaranteed [HKSample]?, @guaranteed Error?) -> () + 132 (<compiler-generated>:0) 7 libdispatch.dylib 0x0000000193359a84 _dispatch_call_block_and_release + 32 (init.c:1466) 8 libdispatch.dylib 0x000000019335b81c _dispatch_client_callout + 20 (object.m:559) 9 libdispatch.dylib 0x0000000193363004 _dispatch_lane_serial_drain + 620 (inline_internal.h:2557) 10 libdispatch.dylib 0x0000000193363c34 _dispatch_lane_invoke + 456 (queue.c:3862) 11 libdispatch.dylib 0x000000019336e4bc _dispatch_workloop_worker_thread + 764 (queue.c:6589) 12 libsystem_pthread.dylib 0x00000001df2d27a4 _pthread_wqthread + 276 (pthread.c:2437) 13 libsystem_pthread.dylib 0x00000001df2d974c start_wqthread + 8 (:-1) Thread 17 crashed with ARM Thread State (64-bit): x0: 0x0000000000000000 x1: 0x00000001e2b66eb7 x2: 0x0000000000000002 x3: 0x00000001a2d8e1b0 x4: 0x00000000000062dc x5: 0x00000001012329c0 x6: 0x0000000000000142 x7: 0x0000000000000000 x8: 0x000000016facf0e0 x9: 0x0000000000000000 x10: 0x0000000297a6fa57 x11: 0x000f000280cf4a00 x12: 0x000000000000000a x13: 0x0000000280cf4a90 x14: 0x000000009f285800 x15: 0x00000001f5dfdee0 x16: 0x00000001a823473c x17: 0xe52c29819e292a34 x18: 0x0000000000000000 x19: 0x0000000197650b94 x20: 0x00000035312f3131 x21: 0x00000002802ee2e0 x22: 0x0000000197650b40 x23: 0x00000001f64c5848 x24: 0x0000000101210d70 x25: 0x000000016face650 x26: 0x00000002805ed140 x27: 0xa119cee65a67aed3 x28: 0xe500000000000000 fp: 0x000000016face740 lr: 0x0000000100cda868 sp: 0x000000016face650 pc: 0x0000000100cdaaa4 cpsr: 0x80000000 esr: 0xf2000001 Address size fault
排查建议
- 确认
workout对象的安全性:检查workout是否为隐式解包的Optional类型(如HKWorkout!),如果workout本身为nil,访问startDate时就会崩溃,但编译器优化可能导致行号偏移到if let行。建议先对workout做安全解包:if let workout = workout { ... }。 - 验证能量单位的有效性:
HKUnit.kilocalorie()在特殊系统版本或配置下可能存在异常,改为显式创建单位HKUnit(unitString: "kcal"),并添加非空判断。同时,调用totalEnergyBurned.is(compatibleWith:)检查单位兼容性后再转换数值,避免因单位不匹配导致的内部崩溃。 - 排查多线程竞态问题:崩溃发生在后台线程(Thread 17),如果
workout对象在其他线程被释放或修改,可能出现if let判断时非空,但进入代码块后变为nil的情况。确保workout在处理期间是线程安全的,比如使用不可变对象或加锁保护。 - 关闭编译器优化定位真实行号:编译器优化会导致崩溃行号不准确,在Xcode的Build Settings中,将
Swift Compiler - Code Generation > Optimization Level设为No Optimization,重新运行复现问题,确认实际崩溃位置。 - 添加日志追踪对象状态:在
if let前后打印workout和totalEnergyBurned的内存地址及值,观察对象是否被篡改或释放:print("workout: \(workout), addr: \(Unmanaged.passUnretained(workout).toOpaque())") print("totalEnergyBurned before check: \(workout.totalEnergyBurned)") if let totalEnergyBurned = workout.totalEnergyBurned { print("totalEnergyBurned after check: \(totalEnergyBurned), addr: \(Unmanaged.passUnretained(totalEnergyBurned).toOpaque())") model.kilocalorie = Int(round(totalEnergyBurned.doubleValue(for: HKUnit.kilocalorie()))) } - 检查健康数据完整性:用户的HealthKit数据可能存在损坏,导致
totalEnergyBurned返回异常的Optional值。在测试设备中导入异常健康数据,尝试复现崩溃场景。
内容的提问来源于stack exchange,提问作者Haozes
相关产品推荐
相关产品推荐

