Swift中Codable自定义对象编码崩溃问题求助
问题排查与解决方案
一、手动实现Workout Codable时崩溃的原因与解决
崩溃原因
NSKeyedArchiver是Objective-C时代的归档工具,要求归档对象必须遵循NSCoding协议(依赖OC的消息机制)。你的Exercise仅遵循Swift原生的Codable,未实现NSCoding相关方法,导致OC归档工具无法识别,触发unrecognized selector错误。
两种解决方案
方案1:让Exercise同时遵循NSCoding与Codable
如果必须使用NSKeyedArchiver,修改Exercise类:
class Exercise: NSObject, Codable, NSCoding { var name: String var duration: Int // Codable编码键 enum CodingKeys: String, CodingKey { case name, duration } // NSCoding编码实现 func encode(with coder: NSCoder) { coder.encode(name, forKey: CodingKeys.name.rawValue) coder.encode(duration, forKey: CodingKeys.duration.rawValue) } // NSCoding解码实现 required init?(coder: NSCoder) { name = coder.decodeObject(forKey: CodingKeys.name.rawValue) as? String ?? "" duration = coder.decodeInteger(forKey: CodingKeys.duration.rawValue) } // 自定义初始化方法 init(name: String, duration: Int) { self.name = name self.duration = duration } }
方案2:弃用NSKeyedArchiver,全程用Swift Codable处理
既然Exercise已经支持Codable,直接用JSONEncoder编码数组,避免依赖OC工具:
struct Workout: Codable { var exercises: [Exercise] var workoutName: String enum CodingKeys: String, CodingKey { case exercisesData, workoutName } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) // 用JSONEncoder直接编码Exercise数组 let exercisesData = try JSONEncoder().encode(exercises) try container.encode(exercisesData, forKey: .exercisesData) try container.encode(workoutName, forKey: .workoutName) } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let exercisesData = try container.decode(Data.self, forKey: .exercisesData) exercises = try JSONDecoder().decode([Exercise].self, from: exercisesData) workoutName = try container.decode(String.self, forKey: .workoutName) } }
二、自动合成Codable时JSONEncoder报错的原因与排查
报错核心原因
自动合成Codable要求所有属性的类型都必须遵循Codable协议,只要有一个属性不满足,就会触发格式错误。
排查步骤
- 检查属性类型:遍历
Workout的所有属性,确认没有Any、未遵循Codable的自定义类型、OC非兼容类型(如NSMutableDictionary)。 - 检查类/结构体差异:如果
Workout是类且继承了NSObject,自动合成Codable可能受OC属性干扰,建议改为结构体。 - 检查可选类型:如果有
Optional类型属性为nil,JSONEncoder默认会编码为null,但如果后端不接受null值,需手动实现Codable忽略该属性。 - 检查嵌套类型:如果
Workout包含其他自定义对象(如Trainer),必须确保这些对象也遵循Codable。
示例修复
如果Workout包含非Codable的Any类型属性,要么替换为Codable类型,要么手动忽略:
struct Workout: Codable { var exercises: [Exercise] var workoutName: String // 用CodingKeys忽略非Codable属性 private var notes: Any? enum CodingKeys: String, CodingKey { case exercises, workoutName } }
内容的提问来源于stack exchange,提问作者Chubbles
相关产品推荐
相关产品推荐

