App更新后coder.decodeDouble崩溃 原存储值为可选Double类型如何处理
问题根因
- 旧版本中可选
Double?类型的lat归档时会被桥接为NSNumber?存储,lat为nil时要么不写入key,要么写入NSNull实例;后续改为非可选Double后,归档逻辑通常改为直接存储原生浮点基本类型,两种存储格式不统一。 containsValue(forKey:)仅能校验key是否存在,无法校验值的类型是否匹配decodeDouble的要求:只要key存在(哪怕对应值是NSNull、字符串等非数字类型),该方法都会返回true,此时调用decodeDouble会直接抛出ObjC异常导致崩溃。- 之前使用
decodeObject(forKey:)的方案存在警告,是因为当存储的是原生基本类型Double时,该方法无法识别为ObjC对象,苹果已明确该行为未来会升级为错误。
解决方案
方案1:全版本兼容方案(支持所有iOS版本,无警告)
通过ObjC异常捕获包裹解码逻辑,兼容所有新旧存储格式:
- 新增ObjC分类
NSCoder+SafeDecode:
// NSCoder+SafeDecode.h #import <Foundation/Foundation.h> @interface NSCoder (SafeDecode) - (double)safeDecodeDoubleForKey:(NSString *)key defaultValue:(double)defaultValue; @end
// NSCoder+SafeDecode.m #import "NSCoder+SafeDecode.h" @implementation NSCoder (SafeDecode) - (double)safeDecodeDoubleForKey:(NSString *)key defaultValue:(double)defaultValue { @try { return [self decodeDoubleForKey:key]; } @catch (NSException *exception) { return defaultValue; } } @end
- 在Swift桥接文件中引入该头文件,解码时直接调用:
required init(coder aDecoder: NSCoder) { lat = aDecoder.safeDecodeDouble(forKey: "lat", defaultValue: 0) // 其余解码逻辑 }
方案2:纯Swift方案(iOS 11+ 支持)
使用系统提供的类型安全解码API,无需引入ObjC代码:
required init(coder aDecoder: NSCoder) { lat = aDecoder.decodeObject(of: NSNumber.self, forKey: "lat")?.doubleValue ?? 0 // 其余解码逻辑 }
该API会自动适配桥接NSNumber和原生Double两种存储格式,类型不匹配、key不存在、值为nil时都会返回nil,不会抛出异常,也无编译警告。
补充说明
- 若需要兼容iOS 10及更低版本,优先使用方案1,异常捕获的开销极低,不会影响用户体验。
- 后续基础类型有可选改非可选的需求时,不要依赖
containsValue做前置校验,优先使用类型安全的解码API。
内容的提问来源于stack exchange,提问作者Hbombre
相关产品推荐
相关产品推荐

