Core Data: Forcing Propogation of Changes
I just encountered a situation where I was added NSManagedObject instances to a context and the data did not automatically show up in a bound NSArrayController/table view. I think there's an elusive bug to be found here, but the workaround is to do this:NSManagedObjectContext * context; // assume this exists
NSManagedObject * newObject;
newObject = [NSEntityDescription
insertNewObjectForEntityForName: @"Person"
inManagedObjectContext: context];
// force updates to be sent to bound controllers
[context processPendingChanges];
The -processPendingChanges: forces the changes to be processed immediately instead at the end of the current event. Don't get trigger-happy and use it all the time, it's just a workaround if things are acting strange. Also, be aware that it could affect the behavior of automatic undo/redo.

Core Data: Forcing Propogation of Changes
Posted Nov 18, 2005 — 7 comments below
Posted Nov 18, 2005 — 7 comments below
Alex R — Nov 20, 05 555
Any idea what conditions cause this failure?
Scott Stevenson — Nov 21, 05 556
Hannes Petri — Jun 04, 06 1348
Put this in the init-method of the document:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(managedObjectContextUpdatedNotification:) name:NSManagedObjectContextObjectsDidChangeNotification object:[self managedObjectContext]];
And add the method like this:
-(void)managedObjectContextUpdatedNotification:(NSNotification *)notif
{
NSDictionary *userInfo = [notif userInfo];
NSSet *inserted = [userInfo objectForKey:NSInsertedObjectsKey];
if (yourArrayController && inserted && [inserted count]) {
[yourArrayController setSelectedObjects:[inserted allObjects]];
}
}
Mike Margolis — Oct 10, 06 2026
For performance reasons, processPendingChanges: only gets called automatically after event processing in the runloop. It would be too expensive to call it after every single notification, timer invocation, etc. If you are changing your managed object context in response to anything but direct user input you must call processPendingChanges: manually for changes to take effect. Do not abuse this call, it can dramatically slow down your application if, for example, you call it after every single object creation, insertion, modification, or deletion... it is best to do all of your work in a batch before calling processPendingChanges:.
Some examples of when this is needed include a worker thread finishes loading some data and adds new data to the core data managed object context. (Of course, you did add that new data from the *main* runloop, correct?) , or a notification or timer gets triggered and you update some value.
The UI (and many internals of your app) will be out of sync with the expected state of the database....er... object-graph management and persistence thingamagigger (don't call it a database, the core data folks hate that :-D) until the user starts clicking around on the UI... at which point weird unexpected behavior might occur. My suggested approach would be to have the main thread load and process the data that was obtained in the [worker thread/posted notification userinfo / etc] and then have call [moc processPendingChanges:] before returning.
Hopefully this helps, it has saved my butt many times in the past and understanding why this is needed and how often to call it has helped keep my core data apps fast with a consistent 'database'.
Malcolm Hall — Jul 19, 08 6168
- (id)init { self = [super init]; if (self != nil) { // initialization code // maybe this should go in awakeFromNib? timer =[ [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(myTimerFireMethod:) userInfo:nil repeats:YES] retain]; } return self; } - (void)myTimerFireMethod:(NSTimer*)theTimer{ id o = [carArrayController newObject]; [o setValue:@"Ford" forKey:@"makeModel"]; [carArrayController addObject:o]; }
and in MyDocument.h you have:
#import <Cocoa/Cocoa.h> #import "CarArrayController.h" @interface MyDocument : NSPersistentDocument { NSTimer* timer; IBOutlet carArrayController* c; } - (void)myTimerFireMethod:(NSTimer*)theTimer; @end
Finally of course you need to hook up the referencing outlet in the CarArrayController object you added in interface builder to the carArrayController field of File's Owner. So right click the Cars icon and drag from the "New Referencing Outlet" to Files Owner and click carArrayController in the pop up.
So when you add to the array controller in code the UI is instantly updated because all of the binding stuff works as normal. Please tell me if I am wrong about this.
Malcolm Hall — Jul 19, 08 6169
IBOutlet CarArrayController* carArrayController;
Scott Stevenson — Jul 19, 08 6170
First, just to be clear, the phrase "binding context" can mean a lot of different things. At first, I thought you meant the data buffer that's passed in as the "context" argument in a -bind: method, but I'm pretty sure you mean the Managed Object Context that a controller is bound to.
In terms of adding objects, you absolutely can deal directly with the Managed Object Context, and I think this is arugably the preferred way. The NSArrayController is bound to the context, so you're just skipping the middle man. You might have a custom NSArrayController and want to use some logic from that, but that code can be anywhere.
There's nothing necessarily wrong with adding objects in code by talking to the NSArrayController, but it can be a lot slower in some cases (probably not in your example, though), and some of the methods don't take effect until the end of the current user event.
In my opinion, NSArrayController and NSTreeController should mainly be used when you need to connect user interface items to something.