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

Ale*_*exR 3 cocoa-touch core-data objective-c grand-central-dispatch ios

我得到的运行时错误似乎是由于我GCD与自定义NSManagedObjects的错误实现造成的.

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

我通过使用以下提供的托管对象上下文在app委托中创建托管对象上下文UIManagedDocument:self.managedDocument.managedObjectContext.

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

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

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

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

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

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

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

+ (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];
// ...
}
Run Code Online (Sandbox Code Playgroud)

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

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

非常感谢你!

Tom*_*ton 7

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

你没有说你是如何创建MOC的.我建议你应该这样做:

NSManagedObjectContext *context = [[NSManagedObjectContext alloc]
    initWithConcurrencyType: NSMainQueueConcurrencyType];
Run Code Online (Sandbox Code Playgroud)

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

[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];
    // ...
}];
Run Code Online (Sandbox Code Playgroud)

这将确保即使您在后台队列中,MOC的工作也会在主线程上进行.(从技术上讲,它实际上意味着MOC在后台的工作将与它在主队列上的工作正确同步,但结果是相同的:这是安全的方法).

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