开发者

NSFetchedResultsController not displaying changes from background thread

开发者 https://www.devze.com 2023-03-17 04:54 出处:网络
My app is using an NSFetchedResultsController tied to a Core Data store and it has worked well so far, but I am now trying to make the update code asynchronous and I am having issues. I have created a

My app is using an NSFetchedResultsController tied to a Core Data store and it has worked well so far, but I am now trying to make the update code asynchronous and I am having issues. I have created an NSOperation sub-class to do my updates in and am successfully adding this new object to an NSOperationQueue. The updates code is executing as I expect it to and I have verified this through debug logs and by examining the SQLite store after it runs.

The problem is that after my background operation completes, the new (or updated) items do not appear in my UITableView. Based on my limited understanding, I believe that I need to notify the main managedObjectContext that changes have occurred so that they may be merged in. My notification is firing, nut no new items appear in the tableview. If I stop the app and restart it, the objects appear in the tableview, leading me to believe that they are being inserted to the core data store successfully but are not being merged into the managedObjectContext being used on the main thread.

I have included a sample of my operation's init, main and notification methods. Am I missing something important or maybe going about this in the wrong way? Any help would be greatly appreciated.

- (id)initWithDelegate:(AppDelegate *)theDelegate
{
    if (!(self = [super init])) return nil;
    delegate = theDelegate;
    return self;
}

- (void)main
{
    [self setUpdateContext:[self managedObjectContext]];
    NSManagedObjectContext *mainMOC = [self newContextToMainStore];
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center addObserver:self 
               selector:@selector(contextDidSave:) 
                   name:NSManagedObjectContextDidSaveNotification 
                 object:updateContext];
    [self setMainContext:mainMOC];

    // Create/update objects with mainContext.

    NSError *error = nil;
    if (![[self mainContext] save:&error]) {
        DLog(@"Error saving event to CoreData store");
    }
    DLog(@"Core Data context saved");
}


- (void)contextDidSave:(NSNotification*)notification
{
    DLog(@"Notification fired.");
    SEL selector = @selector(mergeChangesFromContextDidSaveNotification:);
    [[delegate managedObjectContext] performSelectorOnMainThread:selector
                                            开发者_Python百科          withObject:notification
                                                   waitUntilDone:YES]; 
}

While debugging, I examined the notification object that is being sent in contextDidSave: and it seems to contain all of the items that were added (excerpt below). This continues to make me think that the inserts/updates are happening correctly but somehow the merge is not being fired.

NSConcreteNotification 0x6b7b0b0 {name = NSManagingContextDidSaveChangesNotification; object = <NSManagedObjectContext: 0x5e8ab30>; userInfo = {
inserted = "{(\n    <GCTeam: 0x6b77290> (entity: GCTeam; id: 0xdc5ea10 <x-coredata://F4091BAE-4B47-4F3A-A008-B6A35D7AB196/GCTeam/p1> ; data: {\n    changed = 


The method that receives your notification must indeed notify your context, you can try something like this, which is what I am doing in my application:

 - (void)updateTable:(NSNotification *)saveNotification
  {
    if (fetchedResultsController == nil)
    {
       NSError *error;
    if (![[self fetchedResultsController] performFetch:&error]) {
    //Update to handle the error appropriately.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        exit(-1);  // Fail
    }       
}
else
{
    NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
    // Merging changes causes the fetched results controller to update its results
    [context mergeChangesFromContextDidSaveNotification:saveNotification];
    // Reload your table view data  
    [self.tableView reloadData];
}
}

Hope that helps.


Depending on the specifics of what you are doing, you may be going about this the wrong way.

For most cases, you can simply assign a delegate using NSFetchedResultsControllerDelegate. You provide an implementation for one of the methods specified in "respondingToChanges" depending on your needs, and then send the tableView a reloadData message.


The answer turned out to be unrelated to the posted code which ended up working as I expected. For reasons that I am still not entirely sure of, it had something to do with the first launch of the app. When I attempted to run my update operation on launches after the Core Data store was created, it worked as expected. I solved the problem by pre-loading a version of the sqlite database in the app so that it did not need to create an empty store on first launch. I wish I understood why this solved the problem, but I was planning on doing this either way. I am leaving this here in the hope that someone else may find it useful and not lose as much time as I did on this.


I've been running into a similar problem in the simulator. I was kicking off an update process when transitioning from the root table to the selected folder. The update process would update CoreData from a web server, save, then merge, but the data didn't show up. If I browsed back and forth a couple times it would show up eventually, and once it worked like clockwork (but I was never able to get that perfect run repeated). This gave me the idea that maybe it's a thread/event timing issue in the simulator, where the table is refreshing too fast or notifications just aren't being queued right or something along those lines. I decided to try running in Instruments to see if I could pinpoint the problem (all CoreData, CPU Monitor, Leaks, Allocations, Thread States, Dispatch, and a couple others). Every time I've done a "first run" with a blank slate since then it has worked perfectly. Maybe Instruments is slowing it down just enough?

Ultimately I need to test on the device to get an accurate test, and if the problem persists I will try your solution in the accepted answer (to create a base sql-lite db to load from).

0

精彩评论

暂无评论...
验证码 换一张
取 消