• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Use of NSManagedObjectContextObjectsDidChangeNotification
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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


  • Prev by Date: Re: NSButton Behaviors
  • Next by Date: Re: Hardware copy protection
  • Previous by thread: Re: Hardware copy protection
  • Next by thread: Mapping ABPerson to Sync Services
  • Index(es):
    • Date
    • Thread