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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:31:56