核心数据和GCD:将正确的托pipe对象上下文传递给自定义NSManagedObjects
我得到了运行时错误,这似乎是由于我的自定义NSManagedObjects与GCD
不正确实现有关。
嵌套在GCD
调用中,我使用的自定义NSManagedObjects(似乎)有自己的托pipe对象上下文(= self.managedObjectContext
)。
我使用UIManagedDocument
: self.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中使用performBlock
或performBlockAndWait
,而不仅仅是后台线程。
第一,
“如何将正确的托pipe对象上下文传递给我的自定义NSManagedObjects。”
我们用NSManagedObjectContext
创buildNSManagedObjectContext
。 而不是相反。 所以,当你有一个NSManagedObject
,你可以通过询问它的属性来访问NSManagedObjectContext
: – managedObjectContext
在Apple文档中列出的– 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. }];
希望这有助于。
- Swift 3:DispatchQueue.main.async {}和DispatcQueue.main.async之间的区别(execute:{})?
- 在dispatch_semaphore_dispose上的EXC_BAD_INSTRUCTION(code = EXC_I386_INVOP,子码= 0x0)
- 与GCD同时排队? (iOS 4.2.1)
- imageWithCGImage:GCD内存问题
- 创build具有相同名称的串行队列的两个对象共享同一个队列
- 主线程在viewDidLoad中的一个并发队列上执行dispatch_async,或者在一个方法内部执行
- 在iOS上,你可以做一个同步networking请求(但不是在主线程),仍然进展callback(在一个单独的,非主线程)?
- 与GCD重用UITableViewCell
- GCD和外部线程