核心数据和GCD:将正确的管理对象上下文来定制NSManagedObjects(Core Data

2019-08-16 16:38发布

我得到这似乎从我的不正确实施导致运行时错误GCD与我的自定义NSManagedObjects组合。

在嵌套GCD电话,我使用自定义的NSManagedObjects这(似乎)有自己的管理对象上下文(= self.managedObjectContext )。

我通过所提供的管理对象上下文中创建应用程序委托的管理对象上下文UIManagedDocumentself.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];
// ...});
}

在此主方法我没有被管理对象的上下文的任何参考,我只是调用valueForCoa:convertedTo:其编码如下):

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

valueForCoa是我的自定义的方法子类NSManagedObject ReportedPeriod并使用它(默认)管理对象上下文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.

能否请你帮我这个问题,并给我就如何提高我的代码来传递正确的管理对象上下文的一些建议(或者对如何确保正确的上下文中的所有方法中使用)?

非常感谢你!

Answer 1:

该错误一般地涉及在不同的线程或队列使用管理对象错误地上下文。 您创建的MOC主队列,但你使用它在后台队列中没有考虑到的事实。 这不是错误使用MOC在后台排队,但你必须意识到这一点,并采取准备。

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

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的工作。 (从技术上讲它真正的意思是,商务部在后台工作,将与它的主队列工作正确同步,但结果是一样的:这是做到这一点的安全方法)。

类似的方法是使用NSPrivateQueueConcurrencyType代替。 如果你这样做,你会使用performBlockperformBlockAndWait处处为MOC,不只是在后台线程。



Answer 2:

第一,

“如何通过正确的管理对象上下文到我的自定义NSManagedObjects。”

我们创建NSManagedObjectNSManagedObjectContext 。 不是周围的其他方式。 所以,当你有一个NSManagedObject ,您可以访问NSManagedObjectContext :通过询问其属性– managedObjectContext在上市苹果文档

第二,

当与CoreData工作,多线程可能是一个有点棘手。 特别是对于初学者。 的都是那种你需要照顾的细节。

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

通过使用MagicRecord ,你可以简单地大中央调度与块这样的:

[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.
}];

希望这有助于。



文章来源: Core Data & GCD: Passing the correct managed object context to custom NSManagedObjects