NSManagedObject实现Encodable时NSSet关联编码报错排查
解决NSManagedObject实现Encodable时编码NSSet关联属性的编译错误
错误原因分析
你遇到的这个编译错误,本质是Swift的Encoder无法直接处理Objective-C的NSSet类型,而且编译器没法自动推断devices这个可选NSSet里的具体元素类型(也就是你的Device实体)。因为Encodable协议默认只支持Swift原生的集合类型(比如Array、Set),对NSSet这种OC集合没有内置的编码逻辑,同时你没有明确告诉编译器这个NSSet里装的是遵循Encodable的Device对象,所以它不知道该怎么处理这个属性的编码。
解决步骤
要搞定这个问题,需要分两步走:
1. 让关联的Device类也遵循Encodable协议
首先你的Device实体必须也实现Encodable,不然即使把NSSet转成Swift集合,里面的元素也没法被编码。示例代码如下:
import CoreData import Foundation @objc(Device) public class Device: NSManagedObject, Encodable { @nonobjc public class func fetchRequest() -> NSFetchRequest<Device> { return NSFetchRequest<Device>(entityName: "Device") } // 假设Device有这些属性,根据你的实际情况调整 @NSManaged public var deviceName: String? @NSManaged public var testObject: TestObject? enum CodingKeys: String, CodingKey { case deviceName // 如果不需要编码反向关联的testObject,可以去掉这个key // case testObject } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(deviceName, forKey: .deviceName) // 注意:如果编码反向关联的testObject,可能会出现循环引用问题,需要避免 } }
2. 在TestObject的encode方法中转换NSSet类型
把NSSet?转换成Swift原生的Set<Device>?或者[Device]?,明确元素类型后,Encoder就能正确处理编码了。修改后的TestObject的encode方法如下:
public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy:CodingKeys.self) try container.encode(testName,forKey:.testName) // 把NSSet转成Swift的Set<Device>,再编码 if let devices = devices as? Set<Device> { try container.encode(devices, forKey: .devices) } // 或者转成数组编码,两种方式都可以 // try container.encode(devices?.allObjects as? [Device], forKey: .devices) }
额外注意点
- 如果你的关联关系是双向的(比如Device里也有指向TestObject的属性),编码时一定要避免循环引用,比如不要在Device的encode方法里编码
testObject,否则会导致无限递归编码,运行时崩溃。 - 如果你的NSSet可能为空,上面的代码已经处理了可选值的情况,不会因为nil导致编码失败。
内容的提问来源于stack exchange,提问作者dubbeat
相关产品推荐
相关产品推荐

