如何序列化带额外属性的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
相关产品推荐
相关产品推荐

