JSONEncoder编码继承类触发Thread 1: EXC_BAD_ACCESS错误求助
解决Swift JSONEncoder编码Codable子类时的EXC_BAD_ACCESS错误
这个问题的根源在于Swift的Codable协议对类继承的默认支持有限——自动合成的编码/解码方法无法正确处理父类与子类的属性组合,导致编码子类对象时出现内存访问错误。下面提供两种可行的解决方案:
方案一:用枚举替代类继承(推荐)
枚举+关联值的方式更符合Codable的设计理念,能更清晰地管理不同类型的网络消息,同时避免继承带来的编码问题:
enum NetworkMessage: Codable { // 定义每种消息类型及其关联属性 case addPlayer(playerName: String, playerHashedPw: String) // 可以继续扩展其他消息类型,比如case removePlayer(playerId: Int)等 // 自定义编码键 private enum CodingKeys: String, CodingKey { case type case playerName case playerHashedPw } // 手动实现编码逻辑 func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) switch self { case .addPlayer(let name, let pw): try container.encode("addPlayer", forKey: .type) // 标记消息类型,方便服务器识别 try container.encode(name, forKey: .playerName) try container.encode(pw, forKey: .playerHashedPw) } } // 手动实现解码逻辑(如果需要从服务器响应解码的话) init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let messageType = try container.decode(String.self, forKey: .type) switch messageType { case "addPlayer": let name = try container.decode(String.self, forKey: .playerName) let pw = try container.decode(String.self, forKey: .playerHashedPw) self = .addPlayer(playerName: name, playerHashedPw: pw) default: throw DecodingError.dataCorruptedError( forKey: .type, in: container, debugDescription: "Unknown network message type: \(messageType)" ) } } }
使用示例:
do { let netMsg = NetworkMessage.addPlayer(playerName: "Test", playerHashedPw: "hynt87t7t76yt==") let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted let data = try encoder.encode(netMsg) let stringToSend = String(data: data, encoding: .utf8) print(stringToSend ?? "") } catch { print(error) }
方案二:手动实现子类的Codable方法(保留类继承)
如果一定要保留类继承的结构,需要手动实现父类和子类的encode(to:)与init(from:)方法,确保父类和子类的属性都被正确编码:
class NetworkMessage: Codable { var version: Int? required init() { } // 父类的编码键 enum CodingKeys: String, CodingKey { case version } // 手动实现父类编码 func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(version, forKey: .version) } // 手动实现父类解码 required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) version = try container.decodeIfPresent(Int.self, forKey: .version) } } class AddPlayerRequest: NetworkMessage { var playerName: String? var playerHashedPw: String? // 子类的编码键 enum CodingKeys: String, CodingKey { case playerName case playerHashedPw } // 重写编码方法:先编码父类属性,再编码子类属性 override func encode(to encoder: Encoder) throws { try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(playerName, forKey: .playerName) try container.encode(playerHashedPw, forKey: .playerHashedPw) } // 重写解码方法:先解码子类属性,再调用父类初始化 required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) playerName = try container.decodeIfPresent(String.self, forKey: .playerName) playerHashedPw = try container.decodeIfPresent(String.self, forKey: .playerHashedPw) try super.init(from: decoder) } }
测试代码:
do { let netMsg = AddPlayerRequest() netMsg.playerName = "Test" netMsg.playerHashedPw = "hynt87t7t76yt==" netMsg.version = 1 // 可选:设置父类的版本属性 let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted let data = try encoder.encode(netMsg) let stringToSend = String(data: data, encoding: .utf8) print(stringToSend ?? "") } catch { print(error) }
为什么原代码会出错?
你最初的代码依赖Swift自动合成的Codable实现,但自动合成逻辑不会处理类继承的属性组合——当编码子类对象时,JSONEncoder无法正确识别并编码子类的额外属性,最终导致内存访问异常(EXC_BAD_ACCESS)。手动实现编码/解码逻辑可以确保所有属性都被正确处理,避免这个问题。
内容的提问来源于stack exchange,提问作者julienduchow
相关产品推荐
相关产品推荐

