Reputation: 11710
I'm doing some processing inside - prepareForDeletion
in a NSManagedObject
. However when I try to access some properties of this class they are returned to me as nil
(even though I know they are not nil
). So I check if my object is fault [self isFault]
and it returns YES
.
So how can I make the object "not fault" when I'm inside - prepareForDeletion
because I need to access a property?
PhoneGroup.m (NSManagedObject)
- (void)prepareForDeletion {
[super prepareForDeletion];
NSArray *individuals = [self.individuals allObjects]; // individuals is nil
for (int i = 0; i < [individuals count]; i++) {
Individual *individual = [individuals objectAtIndex:i];
BOOL individualExistsInOtherGroups = [individual.phoneGroups count] > 1;
BOOL individualAddedAsAdditionalContactToReminder = [individual.reminders count] > 0;
// We can delete the individual if individual does not exist in other groups and is not added to any reminders
if (!individualExistsInOtherGroups && !individualAddedAsAdditionalContactToReminder && ![individual isDeleted]) {
[[CoreDataHelper sharedInstance] deleteEntity:individual inManagedObjectContext:self.managedObjectContext];
}
}
// Post a notification to notify all interested VC so they can refresh the GUI
[[NSNotificationCenter defaultCenter] postNotificationName:RMContactDeletedNotification object:self];
DLog(@"Contact deleted notification sent");
}
Model information:
PhoneGroup
has an inverse to-many relationship to Individual
with delete rule nullify.
Individual
has an inverse to-many relationship to PhoneGroup
with delete rule nullify.
Contact
is a superclass for Individual
s and PhoneGroup
s
Here's an image to visualize better.
PhoneGroup
only seems to fault when there is a relationship to a Reminder
. If a PhoneGroup
does not have a relationship to a Reminder
when it is being deleted it is not fault.
Reminder
has an optional inverse to-many relationship to Contact
with delete rule nullify.
Contact
has an optional inverse to-many relationship to Reminder
with delete rule nullify.
What gives of this?
I tried Dan Shelly's alternative solution to do a fetch of Individual
s however even the self.managedObjectContext
of the NSManagedObject
(PhoneGroup
) is nil
inside - prepareForDeletion
what does this imply?
Upvotes: 1
Views: 1847
Reputation: 700
Late in the game, but I just had a few issues similar to that.
Is your store on iCloud?
My problems were related to the fact that I was using coredata with iCloud.
Deleted objects coming from iCloud may already have their properties and related entities no more accessible, even before any call to mergeChangesFromContextDidSaveNotification
.
I ended up not using any deleted object directly, nor relying on prepareForDeletion
for accessing the data I needed.
Instead, I cache this information (in user settings is good for my use case), keyed with the objectID. Upon deletion, I retrieve that information using the objectID found in the NSDeletedObjectsKey
of the notification user info. The cached info is deleted after that.
Upvotes: 0
Reputation: 6011
Why does
[self.individuals allObjects]
return nil?
Edit:
After discovering that your managedObjectContext
is nil
it is safe to say that your object has been disowned by its context.
This might be caused by a context reset
or deallocation, while you maintain a strong reference to your object.
However this won't explain how this happen inside the prepareForDeletion
method.
This method is invoked when a live context is deleting an object ([context deleteObject:...]
).
This would mean that either you invoke prepareForDeletion
manually ([object prepareForDeletion]
), or that another thread is reseting/deallocating your context in mid-delete.
Note: The below code will work given that you have a valid
objectID
and a livemanagedObjectContext
set on your object.
However, I might have a work around as the way you access your data is somewhat inefficient (will explain shortly):
If your deleted object (PhoneGroup
) individuals
relationship is not faulted, CoreData will forst access the store to fault the relationship (1st trip). you will then have faults as objects contained in that relationship (N objects), your loop will access them one by one (N trips to the store). still in the loop, you access each object for its relationships (2 relations ships, phoneGroups
and reminders
==> 2 trips to the store PER ITEM).
In conclusion: 1 + N + 2*N = 3*N +1 trips to the store (worst case).
This could be accomplished in a much simpler way:
* create this fetch request:
NSFetchRequest* r = [[NSFetchRequest alloc] initWithEntityName:@"Individual"];
[r setPredicate:[NSPredicate predicateWithFormat:@"%@ IN phoneGroups AND phoneGroups.@count == 1 AND reminders.@count == 0",[self objectID]]];
[r setIncludesPropertyValues:YES];
[r setReturnsObjectsAsFaults:NO];
[r setIncludesPendingChanges:YES];
* now all that is left is:
NSManagedObjectContext* context = [self managedObjectContext];
NSArray* individuals = [context executeFetchRequest:r error:NULL];
for (Individual* individual in individuals) {
[context deleteObject:individual];
}
this is accomplished with a single (1) trip to the store in the worst case, and since you don't rely on you relationship directly, this should work as long as your object has its objectID
.
Apendix:
Current implementation performance (SQLite debug):
//2013-05-19 21:34:42.700 P[9666:c07] CoreData: sql: SELECT 0, t0.Z_PK FROM Z_2PHONEGROUPS t1 JOIN ZCONTACT t0 ON t0.Z_PK = t1.Z_2INDIVIDUALS WHERE t1.Z_3PHONEGROUPS = ?
//2013-05-19 21:34:42.701 P[9666:c07] CoreData: annotation: sql connection fetch time: 0.0004s
//2013-05-19 21:34:42.701 P[9666:c07] CoreData: annotation: total fetch execution time: 0.0008s for 3 rows.
//2013-05-19 21:34:42.715 P[9666:c07] CoreData: annotation: to-many relationship fault "individuals" for objectID 0x8572b70 <x-coredata://CBE585F2-552D-4FDE-AAEA-2C9C7984FAC9/PhoneGroup/p21> fulfilled from database. Got 3 rows
//2013-05-19 21:34:42.716 P[9666:c07] CoreData: sql: SELECT t0.Z_ENT, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.ZCONTACTID, t0.ZVISIBLE FROM ZCONTACT t0 WHERE t0.Z_PK = ?
//2013-05-19 21:34:42.717 P[9666:c07] CoreData: annotation: sql connection fetch time: 0.0005s
//2013-05-19 21:34:42.717 P[9666:c07] CoreData: annotation: total fetch execution time: 0.0008s for 1 rows.
//2013-05-19 21:34:42.718 P[9666:c07] CoreData: annotation: fault fulfilled from database for : 0x818cad0 <x-coredata://CBE585F2-552D-4FDE-AAEA-2C9C7984FAC9/Individual/p4>
//2013-05-19 21:34:42.718 P[9666:c07] CoreData: sql: SELECT 0, t0.Z_PK FROM Z_2PHONEGROUPS t1 JOIN ZCONTACT t0 ON t0.Z_PK = t1.Z_3PHONEGROUPS WHERE t1.Z_2INDIVIDUALS = ?
//2013-05-19 21:34:42.719 P[9666:c07] CoreData: annotation: sql connection fetch time: 0.0004s
//2013-05-19 21:34:42.719 P[9666:c07] CoreData: annotation: total fetch execution time: 0.0008s for 1 rows.
//2013-05-19 21:34:42.719 P[9666:c07] CoreData: annotation: to-many relationship fault "phoneGroups" for objectID 0x818cad0 <x-coredata://CBE585F2-552D-4FDE-AAEA-2C9C7984FAC9/Individual/p4> fulfilled from database. Got 1 rows
//2013-05-19 21:34:42.720 P[9666:c07] CoreData: sql: SELECT 0, t0.Z_PK FROM ZREMINDER t0 WHERE t0.ZCONTACT = ?
//2013-05-19 21:34:42.721 P[9666:c07] CoreData: annotation: sql connection fetch time: 0.0005s
//2013-05-19 21:34:42.721 P[9666:c07] CoreData: annotation: total fetch execution time: 0.0008s for 0 rows.
//2013-05-19 21:34:42.721 P[9666:c07] CoreData: annotation: to-many relationship fault "reminders" for objectID 0x818cad0 <x-coredata://CBE585F2-552D-4FDE-AAEA-2C9C7984FAC9/Individual/p4> fulfilled from database. Got 0 rows
//2013-05-19 21:34:42.722 P[9666:c07] CoreData: sql: SELECT t0.Z_ENT, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.ZCONTACTID, t0.ZVISIBLE FROM ZCONTACT t0 WHERE t0.Z_PK = ?
//2013-05-19 21:34:42.722 P[9666:c07] CoreData: annotation: sql connection fetch time: 0.0004s
//2013-05-19 21:34:42.722 P[9666:c07] CoreData: annotation: total fetch execution time: 0.0008s for 1 rows.
//2013-05-19 21:34:42.723 P[9666:c07] CoreData: annotation: fault fulfilled from database for : 0x818cb90 <x-coredata://CBE585F2-552D-4FDE-AAEA-2C9C7984FAC9/Individual/p16>
//2013-05-19 21:34:42.723 P[9666:c07] CoreData: sql: SELECT 0, t0.Z_PK FROM Z_2PHONEGROUPS t1 JOIN ZCONTACT t0 ON t0.Z_PK = t1.Z_3PHONEGROUPS WHERE t1.Z_2INDIVIDUALS = ?
//2013-05-19 21:34:42.724 P[9666:c07] CoreData: annotation: sql connection fetch time: 0.0004s
//2013-05-19 21:34:42.724 P[9666:c07] CoreData: annotation: total fetch execution time: 0.0008s for 1 rows.
//2013-05-19 21:34:42.724 P[9666:c07] CoreData: annotation: to-many relationship fault "phoneGroups" for objectID 0x818cb90 <x-coredata://CBE585F2-552D-4FDE-AAEA-2C9C7984FAC9/Individual/p16> fulfilled from database. Got 1 rows
//2013-05-19 21:34:42.725 P[9666:c07] CoreData: sql: SELECT 0, t0.Z_PK FROM ZREMINDER t0 WHERE t0.ZCONTACT = ?
//2013-05-19 21:34:42.725 P[9666:c07] CoreData: annotation: sql connection fetch time: 0.0004s
//2013-05-19 21:34:42.726 P[9666:c07] CoreData: annotation: total fetch execution time: 0.0008s for 0 rows.
//2013-05-19 21:34:42.726 P[9666:c07] CoreData: annotation: to-many relationship fault "reminders" for objectID 0x818cb90 <x-coredata://CBE585F2-552D-4FDE-AAEA-2C9C7984FAC9/Individual/p16> fulfilled from database. Got 0 rows
//2013-05-19 21:34:42.727 P[9666:c07] CoreData: sql: SELECT t0.Z_ENT, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.ZCONTACTID, t0.ZVISIBLE FROM ZCONTACT t0 WHERE t0.Z_PK = ?
//2013-05-19 21:34:42.727 P[9666:c07] CoreData: annotation: sql connection fetch time: 0.0004s
//2013-05-19 21:34:42.727 P[9666:c07] CoreData: annotation: total fetch execution time: 0.0008s for 1 rows.
//2013-05-19 21:34:42.728 P[9666:c07] CoreData: annotation: fault fulfilled from database for : 0x818cba0 <x-coredata://CBE585F2-552D-4FDE-AAEA-2C9C7984FAC9/Individual/p17>
//2013-05-19 21:34:42.728 P[9666:c07] CoreData: sql: SELECT 0, t0.Z_PK FROM Z_2PHONEGROUPS t1 JOIN ZCONTACT t0 ON t0.Z_PK = t1.Z_3PHONEGROUPS WHERE t1.Z_2INDIVIDUALS = ?
//2013-05-19 21:34:42.729 P[9666:c07] CoreData: annotation: sql connection fetch time: 0.0004s
//2013-05-19 21:34:42.729 P[9666:c07] CoreData: annotation: total fetch execution time: 0.0008s for 4 rows.
//2013-05-19 21:34:42.729 P[9666:c07] CoreData: annotation: to-many relationship fault "phoneGroups" for objectID 0x818cba0 <x-coredata://CBE585F2-552D-4FDE-AAEA-2C9C7984FAC9/Individual/p17> fulfilled from database. Got 4 rows
//2013-05-19 21:34:42.730 P[9666:c07] CoreData: sql: SELECT 0, t0.Z_PK FROM ZREMINDER t0 WHERE t0.ZCONTACT = ?
//2013-05-19 21:34:42.730 P[9666:c07] CoreData: annotation: sql connection fetch time: 0.0004s
//2013-05-19 21:34:42.731 P[9666:c07] CoreData: annotation: total fetch execution time: 0.0007s for 0 rows.
//2013-05-19 21:34:42.731 P[9666:c07] CoreData: annotation: to-many relationship fault "reminders" for objectID 0x818cba0 <x-coredata://CBE585F2-552D-4FDE-AAEA-2C9C7984FAC9/Individual/p17> fulfilled from database. Got 0 rows
Proposed implementation performance:
//2013-05-19 21:13:26.734 P[9609:c07] CoreData: sql: SELECT t0.Z_ENT, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.ZCONTACTID, t0.ZVISIBLE FROM ZCONTACT t0 JOIN Z_2PHONEGROUPS t1 ON t0.Z_PK = t1.Z_2INDIVIDUALS WHERE ((t1.Z_3PHONEGROUPS = ? AND (SELECT COUNT(t3.Z_3PHONEGROUPS) FROM Z_2PHONEGROUPS t3 WHERE (t0.Z_PK = t3.Z_2INDIVIDUALS) ) = ? AND (SELECT COUNT(t4.Z_PK) FROM ZREMINDER t4 WHERE (t0.Z_PK = t4.ZCONTACT) ) = ?) AND t0.Z_ENT = ?)
//2013-05-19 21:13:26.735 P[9609:c07] CoreData: annotation: sql connection fetch time: 0.0008s
//2013-05-19 21:13:26.736 P[9609:c07] CoreData: annotation: total fetch execution time: 0.0012s for 2 rows.
Data construction (- (void) synthesizePhoneGroupsAndIndividuals:(NSManagedObjectContext*)context
):
NSMutableArray* individuals = [NSMutableArray new];//[NSEntityDescription insertNewObjectForEntityForName:@"Individual" inManagedObjectContext:context];
for (NSUInteger i = 0; i < (3 + arc4random() % 100); ++i) {
Individual* indi = [NSEntityDescription insertNewObjectForEntityForName:@"Individual" inManagedObjectContext:context];
indi.contactID = [NSString stringWithFormat:@"cid-%u",i];
[individuals addObject:indi];
}
NSMutableArray* groups = [NSMutableArray new];
for (NSUInteger i = 0; i < 3 + arc4random() % 7; ++i) {
PhoneGroup* group = [NSEntityDescription insertNewObjectForEntityForName:@"PhoneGroup" inManagedObjectContext:context];
group.visible = NO;
NSMutableSet* indis = [group mutableSetValueForKey:@"individuals"];
for (NSUInteger j = 0; j < 3; ++j) {
[indis addObject:[individuals objectAtIndex:(arc4random() % [individuals count])]];
}
[groups addObject:group];
}
[context save:NULL];
Testing code:
NSManagedObjectContext* context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
context.persistentStoreCoordinator = _persistentStoreCoordinator;
[context performBlockAndWait:^{
[self synthesizePhoneGroupsAndIndividuals:context];
[context reset];
}];
[context performBlockAndWait:^{
NSFetchRequest* r = [[NSFetchRequest alloc] initWithEntityName:@"PhoneGroup"];
NSArray* groups = [context executeFetchRequest:r error:NULL];
if ([groups count]) {
PhoneGroup* group = [groups objectAtIndex:(arc4random() % [groups count])];
[context deleteObject:group];
}
}];
Upvotes: 1
Reputation: 12988
I suspect your problem may be caused by calling [super prepareForDeletion];
so early. You should probably do the work you want to do then call [super prepareForDeletion];
.
Upvotes: 0