Use of NSManagedObjectContextObjectsDidChangeNotification
Use of NSManagedObjectContextObjectsDidChangeNotification
- Subject: Use of NSManagedObjectContextObjectsDidChangeNotification
- From: Jerry Krinock <email@hidden>
- Date: Tue, 2 Oct 2007 17:21:54 -0700
I understand the solution given by mmalc a few weeks ago to my thread
"Managed Object wants a per-relationship attribute" (Thanks!) and
have been working with it. I'm using the recommended data model for
this "swimming meet" demo I'm working on:
Event
----- Entry
name ----- Swimmer
entries <->> event -------
orderInE name
swimmer <<-> entries
orderInS
The attributes orderInE (order in event) and orderInS (order in
swimmer) are positive integers, and should always be contiguous
within any event and within any swimmer. For example, if there are 4
swimmers in event, the orderInE of their respective entries should be
1, 2, 3 and 4.
I have found it quite involved to maintain this contiguity, and other
"cascading" business logic required to update related objects after
changes. in this little demo. For example, if an Entry is deleted
or inserted, or is moved (orderInE or orderInS changed), both their
'event' and their 'swimmer' must go through and reorder their
remaining entries, and all the views (and I have several) must be
updated. Another example: If a swimmer or event is deleted, all of
their entries must be deleted. And if entry deletion causes an event
to have no swimmers or a swimmer no events, I might like to delete
them too. Newly-inserted objects should appear at the bottom of
their lists, i.e. have an index one higher than the last entry.
Besides the cascading business logic, there are validation
requirements also; for example, swimmer may not repeatly enter the
same event.
After trying many approaches and force-quitting out of many infinite
loops, I've found myself solving the most difficult business logic
problems by implementing a selector for
NSManagedObjectContextObjectsDidChangeNotification. This
notification has become my "goto" for all the hard stuff.
It makes sense to me, since there are many forces which can change
the model, that this notification is the logical place to do the
"cascading clean up" required in related objects. But since I've not
seen this used in any Examples, I thought I would check in and ask if
anyone else would do it this way, or if there is a more conventional
approach?
Jerry Krinock
In case anyone wants a feel for what it looks like, the following
code is the selector I run for
NSManagedObjectContextObjectsDidChangeNotification. I have written
it using generic class, entity and key names so that it can be re-
used in other projects. To translate from the swimming meet demo,
Event is MO1 (managed object #1)
Entry is Glue (the "glue" object that joins MO1 and MO2)
Swimmer is MO2 (managed object #2)
- (void)modelChanged:(NSNotification*)notification {
NSArray* insertedObjects = [[notification userInfo]
objectForKey:NSInsertedObjectsKey] ;
NSArray* deletedObjects = [[notification userInfo]
objectForKey:NSDeletedObjectsKey] ;
NSArray* updatedObjects = [[notification userInfo]
objectForKey:NSUpdatedObjectsKey] ;
NSManagedObject* object ;
NSEnumerator* e ;
Class glueClass = NSClassFromString(kGlueClassAndEntityName) ;
// DIFFICULT PROBLEM #1
// If mo1 or mo2 instances were deleted, their glues will be
'updated'
// and these updated glues will have no mo1 or no mo2.
// We find those objects and delete them
// Before doing so, we also check to make sure there was at
least one
// deleted object, which tells us that this happened as the
result a
// a deletion and is not a temporary situation due to an object
// under construction.
if ([deletedObjects count]) {
e = [updatedObjects objectEnumerator] ;
while ((object = [e nextObject])) {
if ([object isKindOfClass:[glueClass class]]) {
if (![object valueForKey:[MO2 keyToMe]] || ![object
valueForKey:[MO1 keyToMe]]) {
NSLog(@"DebugLog: 13672 Deleting Glue due to no
mo1 or mo2: %@", object) ;
[[self managedObjectContext] deleteObject:object] ;
}
}
}
}
// DIFFICULT PROBLEM #2
// If glues were added or removed, their mo1 and mo2 will need
their orders rebuilt.
// As it turns out, the deletedObjects and insertObjects, which
are Glue class, arrive
// here with their mo1 and mo2 already set to nil. However, the
two affected mo1 and mo2
// objects (i.e., the -mo1 and -mo2 of the inserted or deleted
Glue), will arrive
// simultaneously in the updatedObjects. So, we collect all the
updatedObjects as affected.
NSManagedObject* innerObject ;
NSMutableSet* affectedMO1Objects = [[NSMutableSet alloc] init] ;
NSMutableSet* affectedMO2Objects = [[NSMutableSet alloc] init] ;
e = [updatedObjects objectEnumerator] ;
while ((object = [e nextObject])) {
if ([object isKindOfClass:[MO1 class]]) {
[affectedMO1Objects addObject:object] ;
}
else if ([object isKindOfClass:[MO2 class]]) {
[affectedMO2Objects addObject:object] ;
}
}
// DIFFICULT PROBLEM #3
// When glue are added to empty mo1 or mo2, they will still have
their initial order
// setting of 2^31-1 (or more in a 64-bit compile?), and since
there are no
// related mo1 or mo2 objects to trigger rebuilding the order,
we have to explicity
// detect those situations and do it.
e = [insertedObjects objectEnumerator] ;
while ((object = [e nextObject])) {
if ([object isKindOfClass:[glueClass class]]) {
if ([[object valueForKey:[MO1 gluesOrderKey]] intValue]
>= 2147483647) {
[affectedMO1Objects addObject:[object valueForKey:
[MO1 keyToMe]]] ;
}
if ([[object valueForKey:[MO2 gluesOrderKey]] intValue]
>= 2147483647) {
[affectedMO2Objects addObject:[object valueForKey:
[MO2 keyToMe]]] ;
}
}
}
// Rebuild orders of affected objects
e = [affectedMO1Objects objectEnumerator] ;
while ((innerObject = [e nextObject])) {
[innerObject
rebuildOrderOfObjectsGlueEntityName:kGlueClassAndEntityName
keyToMe:[MO1 keyToMe]
orderKey:[MO1
gluesOrderKey]] ;
}
e = [affectedMO2Objects objectEnumerator] ;
while ((innerObject = [e nextObject])) {
[innerObject
rebuildOrderOfObjectsGlueEntityName:kGlueClassAndEntityName
keyToMe:[MO2 keyToMe]
orderKey:[MO2
gluesOrderKey]] ;
}
[affectedMO1Objects release] ;
[affectedMO2Objects release] ;
}
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden