如何在Swift的NSCoding初始化中返回预先存在的Core Data对象?

时间:2023-01-13 21:56:25

When an instance of my class is initialized using NSCoding, I want to replace it with an existing object in the Core Data database instead of calling:

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

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

as that would insert a new object into the database.

因为那会将新对象插入数据库。

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
    }
}

This was a fairly common Objective-C pattern, but I can't figure out how to replicate this in Swift 1.2, since assigning another object to self yields a compile error:

这是一个相当常见的Objective-C模式,但我无法弄清楚如何在Swift 1.2中复制它,因为将另一个对象分配给self会产生编译错误:

Cannot assign to 'self' in a method

无法在方法中分配给“self”

and even if it did succeed, it seems Swift requires that I call NSManagedObject's designated initializer which would insert a new object into the database.

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


How do I replace an object with a pre-existing one in the database at initialization time? And if its not possible (please provide a reference if this is the case), what pattern should I be using instead?

1 个解决方案

#1


2  

You can use awakeAfterUsingCoder(_:) for replacing self:

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

You can use this method to eliminate redundant objects created by the coder. For example, if after decoding an object you discover that an equivalent object already exists, you can return the existing object. If a replacement is returned, your overriding method is responsible for releasing the receiver.

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

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
        }
    }
}

#1


2  

You can use awakeAfterUsingCoder(_:) for replacing self:

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

You can use this method to eliminate redundant objects created by the coder. For example, if after decoding an object you discover that an equivalent object already exists, you can return the existing object. If a replacement is returned, your overriding method is responsible for releasing the receiver.

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

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
        }
    }
}