核心数据和GCD:将正确的托pipe对象上下文传递给自定义NSManagedObjects

我得到了运行时错误,这似乎是由于我的自定义NSManagedObjects与GCD不正确实现有关。

嵌套在GCD调用中,我使用的自定义NSManagedObjects(似乎)有自己的托pipe对象上下文(= self.managedObjectContext )。

我使用UIManagedDocumentself.managedDocument.managedObjectContext提供的托pipe对象上下文在应用程序委托中创build托pipe对象上下文。

我不明白如何将正确的托pipe对象上下文传递给我的自定义NSManagedObjects。 我将如何更改我的代码以使用正确的托pipe对象上下文?

这是我的主要方法(在​​视图控制器内):

 dispatch_queue_t queue; queue = dispatch_queue_create("queue", NULL); dispatch_async(queue, ^{ // ... NSDecimalNumber *value = [reportedPeriod valueForCoa:figure.code convertedTo:self.currencySymbol]; // ...}); } 

在这个主要的方法中,我没有任何对托pipe对象上下文的引用,我只是调用valueForCoa:convertedTo:它编码如下):

 - (NSDecimalNumber*)valueForCoa:(NSString*)coaStr convertedTo:(NSString*)targetCurrencyStr { // ... CoaMap *coa = [CoaMap coaItemForString:coaStr inManagedObjectContext:self.managedObjectContext]; // ... } 

valueForCoa是我的自定义子类NSManagedObject ReportedPeriod一个方法,并使用它的(默认)托pipe对象上下文self.managedObjectContext

然后,应用程序通常会在执行获取请求时,在下面的方法中在自定义子类NSManagedObject CoaMap中崩溃:

 + (CoaMap*)coaItemForString:(NSString*)coaStr inManagedObjectContext:(NSManagedObjectContext*)context { NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([self class])]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"coa == %@",coaStr]; request.predicate = predicate; // ** The runtime error occurs in the following line ** NSArray *results = [context executeFetchRequest:request error:nil]; // ... } 

错误消息是: Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: 0x9a8a4a0> was mutated while being enumerated.

你能帮我解决这个问题,并就如何改进我的代码来传递正确的托pipe对象上下文(或如何确保在所有方法中使用正确的上下文)给我一些build议?

非常感谢你!

该错误通常涉及在不同的线程或队列中使用托pipe对象的上下文错误。 你在主队列上创build了MOC,但是你在后台队列上使用它,而没有考虑到这个事实。 在后台队列中使用MOC并不是错误的 ,但是您需要了解这一点并做好准备。

你没有说你如何创build商务部。 我build议你应该这样做:

 NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSMainQueueConcurrencyType]; 

对于主队列并发,您可以在主线程上正常使用它。 当你在你的调度队列中时,执行以下操作:

 [context performBlockAndWait:^{ NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([self class])]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"coa == %@",coaStr]; request.predicate = predicate; NSArray *results = [context executeFetchRequest:request error:nil]; // ... }]; 

这将确保MOC的工作发生在主线程上,即使你在后台队列中。 (从技术上来说,实际上MOC在后台工作的意思是与主队列中的工作正确同步,但结果是一样的:这是安全的方法)。

类似的方法是使用NSPrivateQueueConcurrencyType 。 如果你这样做,你可以在MOC中使用performBlockperformBlockAndWait ,而不仅仅是后台线程。

第一,

“如何将正确的托pipe对象上下文传递给我的自定义NSManagedObjects。”

我们用NSManagedObjectContext创buildNSManagedObjectContext 。 而不是相反。 所以,当你有一个NSManagedObject ,你可以通过询问它的属性来访问NSManagedObjectContext– managedObjectContextApple文档中列出的– managedObjectContext

第二,

使用CoreData时,multithreading可能有点棘手。 特别适合初学者。 这些都是你需要照顾的细节。

我强烈build议你签出Parent-Child NSManagedContext 。 然后,使用MagicRecord

通过使用MagicRecord ,您可以简单地使用以下块进行Grand Central Dispatch:

 [MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext){ // here use the `localContext` as your NSManagedContext and do things in the background. // it will take care of all the rest. }]; 

如果你需要传递NSManagedObject到这个块,记得只传递NSManagedObjectID而不是proprety。

这是一个例子。

 NSManagedObjectID *coaMapID = CoaMap.objectID; [MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext){ coaMap *aCoaMap = (coaMap *)[localContext existingObjectWithID:coaMapID error:&error]; // then use aCoaMap normally. }]; 

希望这有助于。