2014-02-27 47 views
6

我正在使用嵌套上下文模式來支持CoreData的多線程工作。 我有CoredDataManager單例類和環境的inits是:CoreData嵌套上下文:什麼是保存上下文的正確方法?

self.masterContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
self.masterContext.persistentStoreCoordinator = self.persistentStoreCoordinator; 

self.mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; 
self.mainContext.parentContext = self.masterContext; 

有關從Web服務響應我用我的CoreDataManager的API來獲取新的管理範圍內每個插入操作:

- (NSManagedObjectContext *)newManagedObjectContext { 
    NSManagedObjectContext *workerContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
    workerContext.parentContext = self.mainContext; 

    return workerContext; 
} 

它看起來像(PlayerView類是NSManagedObject類的子類):

[PlayerView insertIfNeededByUniqueKey:@"playerViewId" value:playerViewId inBackgroundWithCompletionBlock:^(NSManagedObjectContext *context, PlayerView *playerView) { 
    playerView.playerViewId = playerViewId; 
    playerView.username = playerViewDictionary[@"name"]; 

    [context saveContextWithCompletionBlock:^{ 
     //do something 
    } onMainThread:NO];//block invocation on background thread 
}]; 

saveContextWithCompletionBlock方法實現我ñ的NSManagedObjectContext類別:

- (void)saveContextWithCompletionBlock:(SaveContextBlock)completionBlock onMainThread:(BOOL)onMainThread { 
    __block NSError *error = nil; 

    if (self.hasChanges) { 
     [self performBlock:^{ 
      [self save:&error]; 

      if (error) { 
       @throw [NSException exceptionWithName:NSUndefinedKeyException 
               reason:[NSString stringWithFormat:@"Context saving error: %@\n%@\n%@", error.domain, error.description, error.userInfo] 
              userInfo:error.userInfo]; 
      } 

      if (completionBlock) { 
       if (onMainThread && [NSThread isMainThread]) { 
        completionBlock(); 
       } else if (onMainThread) { 
        dispatch_async(dispatch_get_main_queue(), ^{ 
         completionBlock(); 
        }); 
       } else if ([NSThread isMainThread]) { 
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul), ^{ 
         completionBlock(); 
        }); 
       } else { 
        completionBlock(); 
       } 
      } 
     }]; 
    } 
} 

然後在某個階段,我打電話CoreDataManager的方法來保存主上下文:

- (void)saveMasterContext; { 
    __block NSError *error; 

    [self.mainContext performBlock:^{ 
     [self.mainContext save:&error]; 
     [self treatError:error]; 

     [self.masterContext performBlock:^{ 
      [self.masterContext save:&error]; 
      [self treatError:error]; 
     }]; 
    }]; 
} 

我有兩大類,NSManagedObject的子類 - PlayerView和郵政業。 PlayerView與Post的關係非常多。 PlayerView已保存並確定。郵政從來沒有保存,我得到的錯誤:

CoreData:錯誤:從它的上下文中刪除後,管理對象0x17dadd80(0x17daf930)的變化。

我認爲,問題是在上下文中保存邏輯。

+0

有一個問題是否會執行?否則if([NSThread isMainThread])dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0ul),^ { completionBlock(); });如果生成的線程與Pla​​yerView的回調所在的線程不同,傳入的上下文將不會在正確的線程上,如果您引用它而不是//執行某些操作。 – mitrenegade

回答

0

首先,您遇到的錯誤通常發生在創建新託管對象的上下文在您有機會保存之前消失(釋放)。

其次,確保上下文保存在相應線程中的最佳方法是使用performBlockperformBlockAndWait而不是試圖找出上下文屬於哪個線程。下面是一個示例「保存」功能,安全地保存上下文:

+ (BOOL)save:(NSManagedObjectContext *)context { 
    __block BOOL saved = NO; 
    [context performBlockAndWait: { 
     NSError *error; 
     saved = [context save:&error]; 
     if (!saved) { 
      NSLog("failed to save: %@", error); 
     } 
    }] 
    return saved; 
} 

至於使用嵌套的私有上下文(以與父主線程上下文),我們的團隊經歷了一些問題與模型(不記得確切它是什麼),但我們決定傾聽NSManagedObjectContextDidSaveNotification並使用mergeChangesFromContextDidSaveNotification來更新上下文。

我希望這會有所幫助。

+0

也許,您遇到的問題是,如果您使用多個嵌套上下文(多個子上下文指向一個父上下文),則更改只會自動合併到父上下文中,而不會自動合併到上下文中。因此,兄弟上下文仍然需要通過通知合併更改。 – Dalmazio

0

Bart Jacobs的一篇很好的教程,標題爲:Core Data from Scratch: Concurrency詳細描述了兩種方法,更優雅的解決方案涉及父/子託管對象上下文,包括如何正確保存上下文。

相關問題