3
votes

I have an old project (started on iOS 7) with this simple code:

in AppDelegate I create the managedObjectContext:

- (NSManagedObjectContext *)managedObjectContext {
    // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (!coordinator) {
        return nil;
    }
    _managedObjectContext = [[NSManagedObjectContext alloc] init];
    [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    return _managedObjectContext;
}

Then, I perform an update operation in a view controller:

    [context performBlock:^{
        [context deleteObject:anObject];

        NSError *error = nil;
        if (![context save:&error])
        {
            NSLog(@"Error saving context");
        }
    }];

I'm sure that this code was working correctly on iOS 7.0, but crashes on the performBlock: call on iOS 8 with this error:

Can only use -performBlockAndWait: on an NSManagedObjectContext that was created with a queue.

I can solve the error changing the init method of the NSManagedObjectContext instance like this:

_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

But I don't understand why this behavior changed. In the documentation of NSManagedObjectContext you can read:

A consequence of this is that a context assumes the default owner is the thread or queue that allocated it—this is determined by the thread that calls its init method.

So, in my first example, using a simple init call, the queue owner of the context is the main thread. The performBlock: call was made on the main thread too, so I don't understand why this error. Am I missing something or it's a bug in iOS 8?

1

1 Answers

1
votes

The call -[NSManagedObjectContext init] is just a wrapper for -[NSManagedObjectContext initWithConcurrencyType:] with the argument NSConfinementConcurrencyType. This creates an instance of NSManagedObjectContext that uses the obsolete thread confinement model - which does not uses a queue. Contexts created using init or initWithConcurrencyType: with the value NSConfinementConcurrencyType passed are not compatible with the queue methods performBlock: or performBlockAndWait:.

Create your context using -[NSManagedObjectContext initWithConcurrencyType:] and pass either NSPrivateQueueConcurrencyType or NSMainQueueConcurrencyType as appropriate. The context that is created as a result is compatible with performBlock: and performBlockAndWait: and will uses the queue confinement model that was introduced in iOS 5.