You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

App更新后coder.decodeDouble崩溃 原存储值为可选Double类型如何处理

问题根因
  1. 旧版本中可选Double?类型的lat归档时会被桥接为NSNumber?存储,lat为nil时要么不写入key,要么写入NSNull实例;后续改为非可选Double后,归档逻辑通常改为直接存储原生浮点基本类型,两种存储格式不统一。
  2. containsValue(forKey:)仅能校验key是否存在,无法校验值的类型是否匹配decodeDouble的要求:只要key存在(哪怕对应值是NSNull、字符串等非数字类型),该方法都会返回true,此时调用decodeDouble会直接抛出ObjC异常导致崩溃。
  3. 之前使用decodeObject(forKey:)的方案存在警告,是因为当存储的是原生基本类型Double时,该方法无法识别为ObjC对象,苹果已明确该行为未来会升级为错误。
解决方案

方案1:全版本兼容方案(支持所有iOS版本,无警告)

通过ObjC异常捕获包裹解码逻辑,兼容所有新旧存储格式:

  1. 新增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
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 20:06:05