1

我有CoreData和表與相關的NSFetchedResultsController。控制器具有在主隊列中創建並且只能讀取的上下文。當然,tableviewcontroller實現NSFetchedResultsControllerDelegate協議。NSFetchedResultsControllerDelegate在GCD託管環境中的不可理解的行爲

看看在方法,它實現:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { 
    switch (type) { 
     case NSFetchedResultsChangeInsert: 
      NSLog(@"Inserted in %@", [NSString stringWithUTF8String:dispatch_queue_get_label(dispatch_get_current_queue())]); 
      [_tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 

     case NSFetchedResultsChangeDelete: 
      [_tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 

     case NSFetchedResultsChangeUpdate: 
      NSLog(@"Updated in %@", [NSString stringWithUTF8String:dispatch_queue_get_label(dispatch_get_current_queue())]); 
      [_tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 

     case NSFetchedResultsChangeMove: 
      [_tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      [_tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 
    } 
} 

在後臺我在我的應用程序下載數據並更新數據庫。數據庫的更新總是一樣的。它在我的數據管理方法更新:

- (void)saveDataInBackgroundInForeignContext:(void (^)(NSManagedObjectContext *))saveBlock completion:(void (^)(void))completion { 
   AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; 
    dispatch_async([delegate.dispatcher queueForDataSavingInModel:self.modelName], ^{ 
     [self saveDataInForeignContext:saveBlock]; 

     dispatch_sync(dispatch_get_main_queue(), ^{ 
      completion(); 
     }); 
    }); 
} 


- (void)saveDataInForeignContext:(void (^)(NSManagedObjectContext *))saveBlock { 
   NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; 
   if (coordinator != nil) { 

       NSManagedObjectContext *localContext = [[NSManagedObjectContext alloc] init]; 
       [localContext setPersistentStoreCoordinator:coordinator]; 

       [localContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy]; 
      [self.managedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy]; 
       [[NSNotificationCenter defaultCenter] addObserver:self.managedObjectContext 
                                                selector:@selector(mergeChangesFromContextDidSaveNotification:) 
                                                    name:NSManagedObjectContextDidSaveNotification 
                                                  object:localContext]; 

       saveBlock(localContext); 

       if (localContext.hasChanges) { 

           [self updateLastUpdateDateInConformedUpdatedObjects:localContext]; 

           NSError *error = nil; 
           BOOL success = [localContext save:&error]; 
           if (!success) { 
               NSLog(@"Saving in foreign context failed. %@", error.userInfo); 
           } 
       } 

       [localContext release]; 
   } 
} 

saveBlock我修改方面依賴於從服務器的數據。 所以,在結果我有奇怪的行爲:

請注意第controller:didChangeObject:atIndexPathNSLog第一列表中的方法。讓我們看看日誌:

2012-11-16 02:59:33.376 [27824:5303] Inserted in ru.idecide.saving.calls // WTF WHY?! 
2012-11-16 03:05:56.219 [27824:c07] Updated in com.apple.main-thread 

ru.idecide.saving.calls - queue保存數據。

這並不重要,一切正常,但方法insertRowsAtIndexPaths在插入和(顯然)立即更新後在2-3秒內對UI有影響。爲什麼它會發生,我能做些什麼來避免它?

回答

1

的問題是在這裏:

[[NSNotificationCenter defaultCenter] addObserver:self.managedObjectContext 
              selector:@selector(mergeChangesFromContextDidSaveNotification:) 
               name:NSManagedObjectContextDidSaveNotification 
               object:localContext]; 

你是你的主線程上下文直接鏈接到你的背景隊列上下文。當在後臺線程上運行的localContext發佈通知時,通知將發送給同一隊列中的觀察者(self.managedObjectContext) - 後臺隊列。

您需要將通知傳送到主線程,然後傳送到self.managedObjectContext。給自己一個新的方法來接收背景排隊的通知,並將其轉發給主線程:

- (void)backgroundContextDidSave:(NSNotification *)note { 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     [self.managedObjectContext mergeChangesFromContextDidSaveNotification:note]; 
    }); 
} 

然後使用該方法選擇在您註冊通知:

[[NSNotificationCenter defaultCenter] addObserver:self.managedObjectContext 
              selector:@selector(backgroundContextDidSave:) 
               name:NSManagedObjectContextDidSaveNotification 
               object:localContext]; 
+0

非常感謝,羅布。我想給你的答案投票,但是在這裏新來的,並且爲此留下了小名聲,請稍後再嘗試。 –

+0

@AlexeyRogatkin沒問題。很高興我能幫上忙。 –

+0

你現在爲什麼在更新的主線程中工作? –