Magoo
Magoo

Reputation: 2638

Setting NSManagedObject property stalls app... sometimes

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

Answers (1)

Michał Ciuba
Michał Ciuba

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

Related Questions