2014-02-27 45 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详细描述了两种方法,更优雅的解决方案涉及父/子托管对象上下文,包括如何正确保存上下文。

相关问题