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

如何序列化带额外属性的CLCircularRegion自定义子类?归档丢失父类属性

解决CLCircularRegion子类归档解归档丢失父类属性的问题

我之前也碰到过类似的问题,核心原因是你没搞清楚CoreLocation框架里CLCircularRegion的编码协议支持情况——它遵循的是NSSecureCoding,而不是你可能默认使用的Codable。这就是为什么归档后只有自定义的trialUrl能保留,父类属性全部丢失的原因。下面给你两种解决思路,推荐第一种更稳妥的方案:

方案一:基于NSSecureCoding实现子类的编码解码

既然父类CLCircularRegion本身支持NSSecureCoding,我们的子类直接遵循并实现该协议的方法,同时必须调用父类的编码/解码逻辑,才能保留父类的属性(比如center、radius、identifier)。

自定义子类代码

import CoreLocation

class CustomCircularRegion: CLCircularRegion {
    var trialUrl: String?
    
    // 自定义初始化方法
    init(center: CLLocationCoordinate2D, radius: CLLocationDistance, identifier: String, trialUrl: String?) {
        self.trialUrl = trialUrl
        super.init(center: center, radius: radius, identifier: identifier)
    }
    
    // NSSecureCoding 必需的解码初始化方法
    required init?(coder: NSCoder) {
        // 先解码自定义属性
        self.trialUrl = coder.decodeObject(forKey: "trialUrl") as? String
        // 必须调用父类的解码方法,否则父类属性无法恢复
        super.init(coder: coder)
    }
    
    // NSSecureCoding 编码方法
    override func encode(with coder: NSCoder) {
        // 先编码自定义属性
        coder.encode(trialUrl, forKey: "trialUrl")
        // 调用父类的编码方法,处理父类属性
        super.encode(with: coder)
    }
    
    // 重写该类方法,确保支持安全编码(CoreLocation类都要求这个)
    override class var supportsSecureCoding: Bool {
        return true
    }
}

归档与解归档示例

// 归档到UserDefaults
let testRegion = CustomCircularRegion(
    center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
    radius: 100,
    identifier: "SF_Downtown",
    trialUrl: "https://example.com/trial"
)

if let archivedData = try? NSKeyedArchiver.archivedData(
    withRootObject: testRegion,
    requiringSecureCoding: true
) {
    UserDefaults.standard.set(archivedData, forKey: "CustomCircularRegion")
}

// 从UserDefaults解归档
if let archivedData = UserDefaults.standard.data(forKey: "CustomCircularRegion"),
   let decodedRegion = try? NSKeyedUnarchiver.unarchivedObject(
    ofClass: CustomCircularRegion.self,
    from: archivedData
) {
    print("父类属性identifier:\(decodedRegion.identifier)")
    print("自定义属性trialUrl:\(decodedRegion.trialUrl ?? "无")")
    // 其他父类属性如center、radius也能正常访问
}

方案二:强行适配Codable(不推荐)

如果你的业务必须用Codable,可以通过将父类CLCircularRegion转换为Data(借助NSSecureCoding),再作为自定义子类的一个Codable属性存储。但这种方法有明显缺陷:因为CLCircularRegion的属性都是只读的,在解码时无法直接修改父类实例的属性,实现起来很别扭,容易出问题。

如果你一定要尝试,代码示例大概是这样(仅作参考):

import CoreLocation

class CustomCircularRegion: CLCircularRegion, Codable {
    var trialUrl: String?
    
    private enum CodingKeys: String, CodingKey {
        case trialUrl
        case parentRegionData
    }
    
    // 自定义初始化
    init(center: CLLocationCoordinate2D, radius: CLLocationDistance, identifier: String, trialUrl: String?) {
        self.trialUrl = trialUrl
        super.init(center: center, radius: radius, identifier: identifier)
    }
    
    // Codable解码初始化
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.trialUrl = try container.decodeIfPresent(String.self, forKey: .trialUrl)
        
        // 解码父类的归档数据
        let parentData = try container.decode(Data.self, forKey: .parentRegionData)
        guard let parentRegion = try? NSKeyedUnarchiver.unarchivedObject(ofClass: CLCircularRegion.self, from: parentData) else {
            throw DecodingError.dataCorruptedError(forKey: .parentRegionData, in: container, debugDescription: "无法解码父类区域数据")
        }
        
        // 这里必须调用父类的指定初始化器,但父类属性是只读的,只能用父类实例的属性来初始化
        super.init(center: parentRegion.center, radius: parentRegion.radius, identifier: parentRegion.identifier)
    }
    
    // Codable编码方法
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(trialUrl, forKey: .trialUrl)
        
        // 将当前实例(包含父类属性)归档为Data
        let parentData = try NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: true)
        try container.encode(parentData, forKey: .parentRegionData)
    }
}

关键注意点

  • 绝对不能省略调用父类的encode(with:)和init(coder:)方法,这是保留父类属性的核心。
  • CoreLocation的所有区域类都要求使用安全编码,所以必须确保supportsSecureCoding返回true。

内容的提问来源于stack exchange,提问作者Dipak Singh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:58:37