How can I tell whether an `NSManagedObject` has been deleted? How can I tell whether an `NSManagedObject` has been deleted? ios ios

How can I tell whether an `NSManagedObject` has been deleted?


Checking the context of the managed object seems to work:

if (managedObject.managedObjectContext == nil) {    // Assume that the managed object has been deleted.}

From Apple's documentation on managedObjectContext ...

This method may return nil if the receiver has been deleted from its context.

If the receiver is a fault, calling this method does not cause it to fire.

Both of those seem to be good things.

UPDATE: If you're trying to test whether a managed object retrieved specifically using objectWithID: has been deleted, check out Dave Gallagher's answer. He points out that if you call objectWithID: using the ID of a deleted object, the object returned will be a fault that does not have its managedObjectContext set to nil. Consequently, you can't simply check its managedObjectContext to test whether it has been deleted. Use existingObjectWithID:error: if you can. If not, e.g., you're targeting Mac OS 10.5 or iOS 2.0, you'll need to do something else to test for deletion. See his answer for details.


UPDATE: An improved answer, based on James Huddleston's ideas in the discussion below.

- (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.");    }}

OLD/DEPRECIATED ANSWER:

I wrote a slightly better method. self is your Core Data class/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.    }}

As James Huddleston mentioned in his answer, checking to see if NSManagedObject's -managedObjectContext returns nil is a "pretty good" way of seeing if a cached/stale NSManagedObject has been deleted from the Persistent Store, but it's not always accurate as Apple states in their docs:

This method may return nil if the receiver has been deleted from its context.

When won't it return nil? If you acquire a different NSManagedObject using the deleted NSManagedObject's -objectID like so:

// 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.");

Here's the printout:

2 - Not deleted.3 - Not deleted.4 - Deleted.5 - Not deleted.6 - Deleted.

As you can see, -managedObjectContext won't always return nil if an NSManagedObject has been deleted from the Persistent Store.


I fear the discussion in the other answers is actually hiding the simplicity of the correct answer. In pretty much all cases, the correct answer is:

if ([moc existingObjectWithID:object.objectID error:NULL]){    // object is valid, go ahead and use it}

The only cases this answer doesn't apply in is:

  1. If you are targetting Mac OS 10.5 or earlier
  2. If you are targetting iOS 2.0 or earlier
  3. If the object/context has not been saved yet (in which case you either don't care because it won't throw a NSObjectInaccessibleException, or you can use object.isDeleted)