如何让Core Data实体类遵守NSSecureCoding协议?
解决Core Data实体类遵守NSSecureCoding的初始化错误与实现方案
错误原因解析
你遇到的Must call a designated initializer of the superclass 'NSManagedObject'错误,核心原因是:
NSManagedObject的唯一指定初始化器是init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?),自定义的普通初始化方法没有调用该方法。@NSManaged修饰的属性由Core Data动态生成,不能在调用父类指定初始化器前直接赋值。
正确实现方案
1. 调整实体类的初始化与NSSecureCoding实现
修改PersonEntity类,移除错误的自定义初始化,改用便捷初始化方法实现属性赋值,同时规范NSSecureCoding的编码/解码逻辑:
import CoreData @objc(PersonEntity) public class PersonEntity: NSManagedObject, NSSecureCoding { public static var supportsSecureCoding: Bool = true enum Key: String { case id = "id" case name = "name" case gender = "gender" case volunteered = "volunteered" case entityName = "PersonEntity" } // 便捷初始化:传入上下文与属性值 convenience init(context: NSManagedObjectContext, id: UUID, name: String, gender: String, volunteered: Bool) { guard let entity = NSEntityDescription.entity(forEntityName: Key.entityName.rawValue, in: context) else { fatalError("无法获取PersonEntity的实体描述") } // 必须先调用父类指定初始化器 super.init(entity: entity, insertInto: context) // 初始化后再设置@NSManaged属性 self.id = id self.name = name self.gender = gender self.volunteered = volunteered } public func encode(with coder: NSCoder) { coder.encode(id, forKey: Key.id.rawValue) coder.encode(name, forKey: Key.name.rawValue) coder.encode(gender, forKey: Key.gender.rawValue) coder.encode(volunteered, forKey: Key.volunteered.rawValue) // 编码实体名称,解码时需依赖它获取实体描述 coder.encode(Key.entityName.rawValue, forKey: "entityName") } required public init?(coder: NSCoder) { // 解码实体名称与上下文(需提前实现CoreData共享栈,示例用CoreDataStack) guard let entityName = coder.decodeObject(forKey: "entityName") as? String, let context = CoreDataStack.shared.persistentContainer.viewContext, let entity = NSEntityDescription.entity(forEntityName: entityName, in: context) else { return nil } // 调用父类指定初始化器,解码时暂不插入上下文 super.init(entity: entity, insertInto: nil) // 安全解码属性(符合NSSecureCoding要求) self.id = coder.decodeObject(of: UUID.self, forKey: Key.id.rawValue) self.name = coder.decodeObject(of: NSString.self, forKey: Key.name.rawValue) as String? self.gender = coder.decodeObject(of: NSString.self, forKey: Key.gender.rawValue) as String? self.volunteered = coder.decodeBool(forKey: Key.volunteered.rawValue) } }
2. 实现对应ValueTransformer
要将PersonEntity作为transformable类型存储,需创建自定义转换器并注册:
class PersonEntityTransformer: ValueTransformer { override class func allowsReverseTransformation() -> Bool { return true } override class func transformedValueClass() -> AnyClass { return PersonEntity.self } override func transformedValue(_ value: Any?) -> Any? { guard let person = value as? PersonEntity else { return nil } do { return try NSKeyedArchiver.archivedData(withRootObject: person, requiringSecureCoding: true) } catch { print("归档PersonEntity失败:\(error)") return nil } } override func reverseTransformedValue(_ value: Any?) -> Any? { guard let data = value as? Data else { return nil } do { return try NSKeyedUnarchiver.unarchivedObject(ofClass: PersonEntity.self, from: data) } catch { print("解档PersonEntity失败:\(error)") return nil } } } // 注册转换器(可在App启动时调用) extension PersonEntityTransformer { static let name = NSValueTransformerName(rawValue: "PersonEntityTransformer") static func register() { let transformer = PersonEntityTransformer() ValueTransformer.setValueTransformer(transformer, forName: name) } }
3. Core Data模型配置
在Core Data模型文件中:
- 找到需要存储
PersonEntity的transformable属性,设置Transformer为PersonEntityTransformer - 勾选
Uses Secure Coding选项
内容的提问来源于stack exchange,提问作者chan
相关产品推荐
相关产品推荐

