如何在 Swift 的 NSCoding 初始化中返回预先存在的核心数据对象?

Posted

技术标签:

【中文标题】如何在 Swift 的 NSCoding 初始化中返回预先存在的核心数据对象?【英文标题】:How do I return a pre-existing Core Data object at NSCoding initialization in Swift? 【发布时间】:2015-07-12 21:04:26 【问题描述】:

当我的类的实例使用NSCoding 初始化时,我想将其替换为Core Data 数据库中的现有 对象,而不是调用:

super.init(entity: ..., insertIntoManagedObjectContext: ...)

因为这会在数据库中插入一个 new 对象。

class MyClass: NSManagedObject, NSCoding 
    required init(coder aDecoder: NSCoder) 
        // Find a preexisting object in the Core Data database

        var ctx = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext!
        var fs = NSFetchRequest(entityName: "MyClass")

        // ... Configure the fetch request based on values in the decoder

        var err: NSErrorPointer = nil
        var results = ctx.executeFetchRequest(fs, error: err)

        // ... Error handling, etc

        newObject = results[0] as! MyClass

        // Attempt to replace self with the new object
        self = newObject // ERROR: "Cannot assign to 'self' in a method"
    

    func encodeWithCoder(aCoder: NSCoder) 
        // Encode some identifying property for this object
    

这是一个 fairly common Objective-C 模式,但我不知道如何在 Swift 1.2 中复制它,因为将另一个对象分配给 self 会产生编译错误:

不能在方法中分配给'self'

即使它确实成功了,似乎 Swift 要求我调用 NSManagedObject 的指定初始化程序,它将一个 new 对象插入数据库。


如何在初始化时将对象替换为数据库中预先存在的对象?如果它 not 可能(如果是这种情况,请提供参考),我应该使用什么模式?

【问题讨论】:

我认为根据语言的“安全特性”,swift 中根本不允许使用这个成语。 【参考方案1】:

您可以使用awakeAfterUsingCoder(_:) 替换self

您可以使用此方法来消除编码器创建的冗余对象。例如,如果在解码对象后发现已经存在等效对象,则可以返回现有对象。如果返回了替换,则您的覆盖方法负责释放接收器。

class Item: NSManagedObject, NSCoding 

    @NSManaged var uuid: String
    @NSManaged var name: String?

    override init(entity: NSEntityDescription, insertIntoManagedObjectContext context: NSManagedObjectContext?) 
        super.init(entity: entity, insertIntoManagedObjectContext: context)
    

    required init(coder aDecoder: NSCoder) 
        let ctx = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext!
        let entity = NSEntityDescription.entityForName("Item", inManagedObjectContext: ctx)!

        // Note: pass `nil` to `insertIntoManagedObjectContext`
        super.init(entity: entity, insertIntoManagedObjectContext: nil)
        self.uuid = aDecoder.decodeObjectForKey("uuid") as! String
    

    func encodeWithCoder(aCoder: NSCoder) 
        aCoder.encodeObject(self.uuid, forKey: "uuid")
    

    override func awakeAfterUsingCoder(aDecoder: NSCoder) -> AnyObject? 
        let ctx = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext!
        let fetch = NSFetchRequest(entityName: "Item")
        fetch.predicate = NSPredicate(format: "uuid == %@", self.uuid)

        if let obj =  ctx.executeFetchRequest(fetch, error: nil)?.first as? Item 
            // OK, the object is still in the storage.
            return obj
        
        else 
            // The object has already been deleted. so insert and use `self`.
            ctx.insertObject(self)
            return self
        
    

【讨论】:

我不敢相信我错过了!谢谢! 万一我需要与上下文中的其他对象建立关系(最好是在初始化时),是否可以在super.init中指定一个上下文,然后在这种情况下调用ctx.deleteObject(self)我们用存储中的一个替换整个对象? 警告对于以后可能会看到这篇文章的任何人:我花了比我愿意承认的时间更多的时间来尝试调试一个似乎是由将nil 传递给insertIntoManagedObjectContext 参数的初始化程序。似乎通过nil(并改为插入awakeAfterUsingCoder)可能会产生一些似乎是错误的副作用,尤其是在多上下文环境中。

以上是关于如何在 Swift 的 NSCoding 初始化中返回预先存在的核心数据对象?的主要内容,如果未能解决你的问题,请参考以下文章

如何让我的简单对象符合 Swift 中的 NSManagedObject 和 NSCoding

Swift 中 SKProduct 的 NSCoding 错误

Swift,NSCoding 保存类的数组不起作用

Swift 继承遵循nscoding的oc类怎么解决require

Swift语言精要 - 序列化和反序列化

使用 NSCoding Swift 保存对象时出现异常