Ich habe eine NSManagedObject
, die gelöscht wurde, und der Kontext, der dieses verwaltete Objekt enthält, wurde gespeichert. Ich verstehe, dass dies isDeleted
zurückgegeben wird, YES
wenn Core Data den persistenten Speicher auffordert, das Objekt beim nächsten Speichervorgang zu löschen. Da das Speichern jedoch bereits erfolgt ist, wird isDeleted
zurückgegeben NO
.
Was ist ein guter Weg, um festzustellen, ob ein NSManagedObject
gelöscht wurde, nachdem sein enthaltener Kontext gespeichert wurde?
(Falls Sie sich fragen, warum das Objekt, das auf das gelöschte verwaltete Objekt verweist, die Löschung noch nicht kennt, liegt dies daran, dass das Löschen und Speichern des Kontexts von einem Hintergrundthread initiiert wurde, der das Löschen und Speichern mit durchgeführt hat performSelectorOnMainThread:withObject:waitUntilDone:
.)
quelle
isInserted
die ein BOOL auf NSManagedObject zurückgibt, was meines Wissens dasselbe bedeutet. Es ist wahrscheinlich etwas sauberer, es für diesen Fall zu verwenden.isInserted
ist nur JA, bis das Objekt gespeichert ist, und dann wird es NEIN. Die Dokumentation sagt dies nicht, aber meine Tests beweisen es.UPDATE: Eine verbesserte Antwort, basierend auf James Huddlestons Ideen in der folgenden Diskussion.
- (BOOL)hasManagedObjectBeenDeleted:(NSManagedObject *)managedObject { /* Returns YES if |managedObject| has been deleted from the Persistent Store, or NO if it has not. NO will be returned for NSManagedObject's who have been marked for deletion (e.g. their -isDeleted method returns YES), but have not yet been commited to the Persistent Store. YES will be returned only after a deleted NSManagedObject has been committed to the Persistent Store. Rarely, an exception will be thrown if Mac OS X 10.5 is used AND |managedObject| has zero properties defined. If all your NSManagedObject's in the data model have at least one property, this will not be an issue. Property == Attributes and Relationships Mac OS X 10.4 and earlier are not supported, and will throw an exception. */ NSParameterAssert(managedObject); NSManagedObjectContext *moc = [self managedObjectContext]; // Check for Mac OS X 10.6+ if ([moc respondsToSelector:@selector(existingObjectWithID:error:)]) { NSManagedObjectID *objectID = [managedObject objectID]; NSManagedObject *managedObjectClone = [moc existingObjectWithID:objectID error:NULL]; if (!managedObjectClone) return YES; // Deleted. else return NO; // Not deleted. } // Check for Mac OS X 10.5 else if ([moc respondsToSelector:@selector(countForFetchRequest:error:)]) { // 1) Per Apple, "may" be nil if |managedObject| deleted but not always. if (![managedObject managedObjectContext]) return YES; // Deleted. // 2) Clone |managedObject|. All Properties will be un-faulted if // deleted. -objectWithID: always returns an object. Assumed to exist // in the Persistent Store. If it does not exist in the Persistent // Store, firing a fault on any of its Properties will throw an // exception (#3). NSManagedObjectID *objectID = [managedObject objectID]; NSManagedObject *managedObjectClone = [moc objectWithID:objectID]; // 3) Fire fault for a single Property. NSEntityDescription *entityDescription = [managedObjectClone entity]; NSDictionary *propertiesByName = [entityDescription propertiesByName]; NSArray *propertyNames = [propertiesByName allKeys]; NSAssert1([propertyNames count] != 0, @"Method cannot detect if |managedObject| has been deleted because it has zero Properties defined: %@", managedObject); @try { // If the property throws an exception, |managedObject| was deleted. (void)[managedObjectClone valueForKey:[propertyNames objectAtIndex:0]]; return NO; // Not deleted. } @catch (NSException *exception) { if ([[exception name] isEqualToString:NSObjectInaccessibleException]) return YES; // Deleted. else [exception raise]; // Unknown exception thrown. } } // Mac OS X 10.4 or earlier is not supported. else { NSAssert(0, @"Unsupported version of Mac OS X detected."); } }
ALTE / VERRÜCKTE ANTWORT:
Ich habe eine etwas bessere Methode geschrieben.
self
ist Ihre Core Data-Klasse / Controller.- (BOOL)hasManagedObjectBeenDeleted:(NSManagedObject *)managedObject { // 1) Per Apple, "may" be nil if |managedObject| was deleted but not always. if (![managedObject managedObjectContext]) return YES; // Deleted. // 2) Clone |managedObject|. All Properties will be un-faulted if deleted. NSManagedObjectID *objectID = [managedObject objectID]; NSManagedObject *managedObjectClone = [[self managedObjectContext] objectWithID:objectID]; // Always returns an object. Assumed to exist in the Persistent Store. If it does not exist in the Persistent Store, firing a fault on any of its Properties will throw an exception. // 3) Fire faults for Properties. If any throw an exception, it was deleted. NSEntityDescription *entityDescription = [managedObjectClone entity]; NSDictionary *propertiesByName = [entityDescription propertiesByName]; NSArray *propertyNames = [propertiesByName allKeys]; @try { for (id propertyName in propertyNames) (void)[managedObjectClone valueForKey:propertyName]; return NO; // Not deleted. } @catch (NSException *exception) { if ([[exception name] isEqualToString:NSObjectInaccessibleException]) return YES; // Deleted. else [exception raise]; // Unknown exception thrown. Handle elsewhere. } }
Wie James Huddleston in seiner Antwort erwähnte, ist die Überprüfung, ob die
-managedObjectContext
Rückgabe von NSManagedObjectnil
eine "ziemlich gute" Methode ist, um festzustellen, ob ein zwischengespeichertes / veraltetes NSManagedObject aus dem persistenten Speicher gelöscht wurde, aber nicht immer korrekt, wie Apple in seinen Dokumenten feststellt:Wann wird es nicht null zurückgeben? Wenn Sie ein anderes NSManagedObject mit den gelöschten NSManagedObjects erwerben, gehen Sie
-objectID
wie folgt vor :// 1) Create a new NSManagedObject, save it to the Persistant Store. CoreData *coreData = ...; NSManagedObject *apple = [coreData addManagedObject:@"Apple"]; [apple setValue:@"Mcintosh" forKey:@"name"]; [coreData saveMOCToPersistentStore]; // 2) The `apple` will not be deleted. NSManagedObjectContext *moc = [apple managedObjectContext]; if (!moc) NSLog(@"2 - Deleted."); else NSLog(@"2 - Not deleted."); // This prints. The `apple` has just been created. // 3) Mark the `apple` for deletion in the MOC. [[coreData managedObjectContext] deleteObject:apple]; moc = [apple managedObjectContext]; if (!moc) NSLog(@"3 - Deleted."); else NSLog(@"3 - Not deleted."); // This prints. The `apple` has not been saved to the Persistent Store yet, so it will still have a -managedObjectContext. // 4) Now tell the MOC to delete the `apple` from the Persistent Store. [coreData saveMOCToPersistentStore]; moc = [apple managedObjectContext]; if (!moc) NSLog(@"4 - Deleted."); // This prints. -managedObjectContext returns nil. else NSLog(@"4 - Not deleted."); // 5) What if we do this? Will the new apple have a nil managedObjectContext or not? NSManagedObjectID *deletedAppleObjectID = [apple objectID]; NSManagedObject *appleClone = [[coreData managedObjectContext] objectWithID:deletedAppleObjectID]; moc = [appleClone managedObjectContext]; if (!moc) NSLog(@"5 - Deleted."); else NSLog(@"5 - Not deleted."); // This prints. -managedObjectContext does not return nil! // 6) Finally, let's use the method I wrote, -hasManagedObjectBeenDeleted: BOOL deleted = [coreData hasManagedObjectBeenDeleted:appleClone]; if (deleted) NSLog(@"6 - Deleted."); // This prints. else NSLog(@"6 - Not deleted.");
Hier ist der Ausdruck:
2 - Not deleted. 3 - Not deleted. 4 - Deleted. 5 - Not deleted. 6 - Deleted.
Wie Sie sehen,
-managedObjectContext
wird nicht immer nil zurückgegeben, wenn ein NSManagedObject aus dem Persistent Store gelöscht wurde.quelle
existingObjectWithID:error:
stattdessen anstelle vonobjectWithID:
und prüfen Sie einfach, ob der Rückgabewert gleich istnil
.-existingObjectWithID:error:
ist ein besserer Weg! :) Ich habe die Antwort geschrieben, um mit Mac OS X 10.5+ kompatibel zu sein, also habe ich diese Methode ignoriert, die nur 10.6+ ist. Und ja, meine Antwort funktioniert nicht für ein Objekt ohne Eigenschaften, obwohl es unwahrscheinlich ist, dass Ihr Datenmodell leere Objekte enthält.objectWithID:
ohne alle Eigenschaften zu überprüfen? (Der Zugriff auf jede Eigenschaft kann für Objekte, die nicht gelöscht wurden , teuer werden .) Wenn es eine einzige Methode gibt, die den Fehler auslöst, können Sie diese Methode einfach für das zurückgegebene Objekt aufrufen,objectWithID:
um festzustellen, ob sie tatsächlich vorhanden ist oder nicht. Ich suchte nach einer solchen Methode, fand aber nichts Offensichtliches.(void)[managedObjectClone valueForKey:[propertyNames objectAtIndex:0]];
einmal aus. Bei einem gelöschten Objekt wird ein Fehler ausgelöst, versucht, aus dem persistenten Speicher zu lesen, undNSObjectInaccessibleException
sofort ausgelöst . Wenn es nicht ausgelöstNSObjectInaccessibleException
wird, bedeutet dies, dass es erfolgreich aus dem persistenten Speicher gelesen wurde und das Objekt nicht gelöscht wurde. Wenn Ihre "zufällige" Eigenschaft bei Index 0 sehr groß sein könnte, wie z. B. 100 MB binäre NSData, wäre eine Optimierung schwierig ...Ich befürchte, dass die Diskussion in den anderen Antworten tatsächlich die Einfachheit der richtigen Antwort verbirgt. In so ziemlich allen Fällen lautet die richtige Antwort:
if ([moc existingObjectWithID:object.objectID error:NULL]) { // object is valid, go ahead and use it }
Die einzigen Fälle, in denen diese Antwort nicht zutrifft, sind:
NSObjectInaccessibleException
, oder Sie können es verwendenobject.isDeleted
)quelle
[moc existingObjectWithID:object.objectID error:NULL])]
sofort veraltet. Selbst wenn wir dies testen und ein "JA" erhalten würden, könnte ein anderer Kontext das Objekt löschen und den Kontext speichern. Das nachfolgendesave
Senden an den vorherigen Kontext löst jetzt eine Ausnahme aus. Schlimmer noch, intern können Core Data Blöcke verwenden und diese synchron an einen anderen Thread senden, wo diese Ausnahme dann auftritt, wodurch das Versuch, Blöcke auf der Aufrufstelle abzufangen, unbrauchbar wird.existingObjectWithID:
wird jeder nacheinander verarbeitet, und das Objekt ist erst nach der Zusammenführung veraltet.Aufgrund meiner jüngsten Erfahrung mit der Implementierung von iCloud in meiner iOS-App, die für die Persistenz auf Core Data basiert, wurde mir klar, dass der beste Weg darin besteht, die Benachrichtigungen des Frameworks zu beobachten. Zumindest besser, als sich auf einige obskure Methoden zu verlassen, die Ihnen möglicherweise mitteilen oder nicht, ob ein verwaltetes Objekt gelöscht wurde.
Für 'reine' Core Data-Apps sollten Sie NSManagedObjectContextObjectsDidChangeNotification beachten im Hauptthread . Das Benutzerinformationswörterbuch der Benachrichtigung enthält Sätze mit den Objekt-IDs der verwalteten Objekte, die eingefügt, gelöscht und aktualisiert wurden.
Wenn Sie die Objekt-ID Ihres verwalteten Objekts in einem dieser Sätze finden, können Sie Ihre Anwendung und Benutzeroberfläche auf eine nette Weise aktualisieren.
Das war's ... Weitere Informationen finden Sie im Apple Core Data Programming Guide, Kapitel Parallelität mit Core Data. Es gibt einen Abschnitt "Verfolgen von Änderungen in anderen Threads mithilfe von Benachrichtigungen". Vergessen Sie jedoch nicht, den vorherigen Abschnitt "Verwenden der Thread-Beschränkung zur Unterstützung der Parallelität" zu überprüfen.
quelle
Verifiziert in Swift 3, Xcode 7.3
Sie können auch einfach
PRINT
die Speicherreferenzen jedes Kontexts anzeigen und überprüfen(a) if the context exists, (b) if the contexts of 2 objects are different
zB :( Buch und Mitglied sind 2 verschiedene Objekte)
print(book.managedObjectContext) print(member.managedObjectContext)
Es würde so etwas drucken, wenn die Kontexte existieren, aber unterschiedlich sind
quelle