2015-07-12 4 views
8

Wenn eine Instanz meiner Klasse initialisiert wird mit NSCoding, mag ich es mit einem bestehenden Objekt in der Core Data Datenbank zu ersetzen, anstatt Aufruf:Wie gebe ich ein bereits vorhandenes Core Data-Objekt bei der NSCoding-Initialisierung in Swift zurück?

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

wie ein neues Objekt einfügen würde in die Datenbank.

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

Das war ein fairly common Objective-C-Muster, aber ich kann nicht herausfinden, wie diese 1.2, in Swift zu replizieren, da ein anderes Objekt zu self Zuweisung eines Kompilierung-Fehler ergibt:

Cannot assign to 'self' in a method

und Selbst wenn es gelingen sollte, scheint Swift es erforderlich zu machen, dass ich 's bezeichneten Initialisierer anrufe, der ein neues Objekt in die Datenbank einfügen würde.


Wie ersetze ich ein Objekt mit einem bereits vorhandenen in der Datenbank zum Zeitpunkt der Initialisierung? Und wenn seine nicht möglich ist (bitte geben Sie eine Referenz, wenn dies der Fall ist), welches Muster sollte ich stattdessen verwenden?

+1

ich denke, das Idiom ist einfach nicht nach dem „Sicherheitsmerkmal“ der Sprache in schnellen erlaubt. – Oxcug

Antwort

1

können Sie awakeAfterUsingCoder(_:) verwenden für den Ersatz 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 
     } 
    } 
} 
+0

Ich kann nicht glauben, dass ich das verpasst habe! Vielen Dank! –

+0

Für den Fall, dass ich Beziehungen zu anderen Objekten im Kontext herstellen muss (vorzugsweise zur Init-Zeit), wäre es in Ordnung, einen Kontext in "super.init" anzugeben und dann 'ctx.deleteObject (self)' im Fall aufzurufen dass wir das gesamte Objekt durch eines aus dem Speicher ersetzen? –

+0

** Warnung ** für alle, die später auf diesen Beitrag kommen: Ich habe mehr Zeit verbracht, als ich zuzugeben bereit bin, ein Problem zu debuggen, das aus der Übergabe von 'nil' an den Initialisierer für das' 'resultiert insertIntoManagedObjectContext' Parameter. Es scheint, dass die Übergabe von 'nil' (und stattdessen das Einfügen in' awakeAfterUsingCoder') einige Nebenwirkungen hat, die Fehler zu sein scheinen, besonders in einer Umgebung mit mehreren Kontexten. –