我如何在 Swift 的 NSCoding 初始化中 return 预先存在的核心数据对象?
How do I return a pre-existing Core Data object at NSCoding initialization in Swift?
当我的 class 实例使用 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
会产生编译错误:
Cannot assign to 'self' in a method
即使它确实成功了,似乎 Swift 要求我调用 NSManagedObject
的指定初始化程序,它将 new 对象插入到数据库。
如何在初始化时用数据库中预先存在的对象替换对象?如果它 不可能 可能(如果是这种情况请提供参考),我应该改用什么模式?
您可以使用 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
}
}
}
当我的 class 实例使用 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
会产生编译错误:
Cannot assign to 'self' in a method
即使它确实成功了,似乎 Swift 要求我调用 NSManagedObject
的指定初始化程序,它将 new 对象插入到数据库。
如何在初始化时用数据库中预先存在的对象替换对象?如果它 不可能 可能(如果是这种情况请提供参考),我应该改用什么模式?
您可以使用 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
}
}
}