Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Data & GCD: Passing the correct managed object context to custom NSManagedObjects

I get runtime errors which seem to result from my incorrect implementation of GCD in combination with my custom NSManagedObjects.

Nested in a GCD call, I am using custom NSManagedObjects which (seem to) have their own managed object contexts (= self.managedObjectContext).

I am creating the managed object context in the app delegate by using the managed object context provided by UIManagedDocument: self.managedDocument.managedObjectContext.

I don't understand how to pass the correct managed object context down to my custom NSManagedObjects. How would I need to change my code to use the correct managed object context?

This is my main method (inside a view controller):

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

In this main method I do not have any reference to a managed object context, I do just call valueForCoa:convertedTo: (which is coded as follows):

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

valueForCoa is a method in my custom subclassed NSManagedObject ReportedPeriod and uses its (default) managed object context self.managedObjectContext.

The app then usually crashes in the custom subclassed NSManagedObject CoaMap in the following method when it executes the fetch request:

+ (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];
// ...
}

The error message is: Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: 0x9a8a4a0> was mutated while being enumerated.

Could you please help me with this issue and give me some suggestions on how to improve my code to pass the correct managed object contexts (or on how to make sure that the correct context is used in all methods)?

Thank you very much!

like image 772
AlexR Avatar asked Jul 03 '26 03:07

AlexR


1 Answers

That error generally relates to using a managed object incorrectly context across different threads or queues. You created the MOC on the main queue, but you're using it on a background queue without considering that fact. It's not wrong to use the MOC on a background queue, but you need to be aware of that and take preparations.

You didn't say how you're creating the MOC. I suggest that you should be doing this:

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

With main queue concurrency you can just use it normally on the main thread. When you're in your dispatch queue though, do this:

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

This will ensure that the MOC's work occurs on the main thread even though you're on a background queue. (Technically what it actually means is that the MOC's work in the background will be correctly synchronized with work it does on the main queue, but the result is the same: this is the safe way to do this).

A similar approach would be to use NSPrivateQueueConcurrencyType instead. If you do that, you'd use performBlock or performBlockAndWait everywhere for the MOC, not just on background threads.

like image 54
Tom Harrington Avatar answered Jul 04 '26 15:07

Tom Harrington



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!