10
votes

I'm using KVO to observe changes on a NSManagedObject. The NSManagedObject I'm observing is part of a NSManagedObject context that is on the main queue.

When I update this object in a background (private queue concurrency type) context and then merge the saved changes to my main queue context (in mergeChangesFromContextDidSaveNotification), KVO notifications fire as expected.

However, I expected that the notifications would only fire for key paths that actually changed and not for all keypaths of the NSManagedObject. I'm receiving KVO notifications for every keypath of my object even though they didn't change.

Is this by design or am I doing something wrong?

Can't see anything in the apple docs....

2
How are you handling the notification? You should be looking at the NSUpdatedObjectsKey of the notification userInfo dictionary.random
I implemented this method: - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;aloo
Now I could look at the change dictionary and look at the old value to see whether its changed from the current value - but why am I getting the KVO notification in the first place if the nsmanagedobject didn't even change for the given keypath?aloo
Should you be listening to NSManagedObjectContextObjectsDidChangeNotification instead of mergeChangesFromContextDidSaveNotification? Maybe when it merges it looks at the merge as "technically" a change??random
I'm not listening to either. Those are CoreData notifications. I'm using KVO to observe changes directly on the NSManagedObject. The change thats causing all these KVO notifications to fire is the result of a merge into the UI context.aloo

2 Answers

3
votes

It is undocumented but observed behaviour on both OS X and iOS that a save counts as a change to the entire NSManagedObject not just differing elements. You can find grumblings about various consequences of that for bindings and the like around this site, on openradar.appspot.com, etc. That the problem also manifests with apparently-spurious KVO firings is completely unsurprising.

Simplest way to handle the problem (well, simplest after 'just redisplay everything on a save' which I find a fine first pass option until someone complains) is to listen for the generic save notification, then call -changedValues on each updated object to pick out the ones you're interested in firing specific updates for.

If that's hopelessly inefficient for your use case, you could make custom accessors (mogenerator is a big help with this) for your properties that collect on the editing thread flags for changes to all the properties you're interested in; and dispatch that as a notification after saving.

Let us for instance say that we have a professional sports team app that is updated constantly with JSON feeds parsed in the background. All display-affecting attributes of the various team, player, game, etc. NSManagedObjects have custom accessors that set flags in a structure of { playerStatsChanged, teamStatsChanged, leagueRankingsChanged, yadayadayadaChanged } corresponding to what pages in the app will need redisplay once the current fetch-and-parse thread completes. Then once it's saved, it fires off a generic 'update these screens' notification with that flag setting structure. You're probably coalescing individual change path notifications into some higher level 'update this screen' type logic somewhere in any case, right? Well, at the property setter level is pretty much the lowest overhead point you can do that at, for most reasonable use cases. Certainly for any recurring fetched update design such as our sports team apps here.

0
votes

You can override the automatic change notifications with manual notifications for only the keys of your choice. Check the detailed documentation here.