Reputation: 2638
I have an app where I loop over some json and set some objects in the background
dispatch_async(CD_QUEUE(), ^{
NSEntityDescription *questionEntity = [NSEntityDescription entityForName:QUESTION inManagedObjectContext:self.backgroundContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
[fetchRequest setEntity:questionEntity];
for (NSInteger i = 0; i < [questionsArray count]; i++) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"uid == %@", questionUID];
[fetchRequest setPredicate:predicate];
NSError *error = nil
NSArray *array = [context executeFetchRequest:fetchRequest error:&error];
MYQuestion *question = nil;
NSLog(@"Error %@",[error localizedDescription]);
if ([array count] == 0)
{
NSLog(@"NIL question");
question = [self newQuestionInContext:context];
question.uid = questionUID;
}
else
question = [array objectAtIndex:0];
if ([questionDataDictionary objectForKey:@"question"] != [NSNull null] && [questionDataDictionary objectForKey:@"question"] != nil)
{
NSLog(@"set question %@",question);
NSLog(@"UID: %@",question.uid);
question.body = [questionDataDictionary objectForKey:@"question"];
NSLog(@"set question finished");
}
and so on....
Sometimes this gets stuck at setting the question.body for the same question every time, but sometimes it completes without an issue. The question is not nil, and as you can see neither is the json object.. so what's the issue here? My app gets stuck at this point and I have no idea how to resolve this (if it happens)
logs....
Error (null)
set question < MYQuestion: 0x17515e20> (entity: MYQuestion; id: 0x17641990 <x-coredata://103ACE29-9CF0-4216-BAF8-A30A04C22B0D/MYQuestion/p12493> ; data: < fault >)
So it faults when I try to access any property on this object... There's no error fetching, but the object is sometimes in fault?? I don't understand.
Upvotes: 0
Views: 85
Reputation: 7944
This is a concurrency issue. In general, NSManagedObjects
are not thread-safe. It means that you can only read and write their properties from a thread in which their NSManagedObjectContext
was created, or in this context's private queue.
You are accessing your managed objects in CD_QUEUE
, so it's up to Grand Central Dispatch to decide in which thread this code will be executed. So there is a chance that these managed objects will be accessed from a "wrong" thread - not the one in which their context
"lives".
Instead of dispatch_async
, use one of performBlock
or performBlockAndWait
methods on your context
. Their purpose is to ensure that managed objects will be accessed in a safe manner:
[context performBlock:^{
//do what you did in dispatch_async block
];
If you want to know more about Core Data and concurrency, read a detailed (although a bit outdated) Apple's Core Data Concurrency Guide and Common Background Practices by objc.io.
For the second part of your question: a object being a fault
does not mean there was an error during the fetch request. It means that the object's properties have not been loaded from the persistent store (like SQLite database) to the memory yet. More on that: Faulting and uniquing
Upvotes: 1