Основные данные NSPrivateQueueConcurrencyType и совместное использование объектов между потоками

В iOS 5 появился новый способ быстрого получения данных в фоновом потоке путем инициализации MOC с помощью NSPrivateQueueConcurrencyType, а затем выполнения выборки в performBlock:

Одним из основных принципов Core Data было то, что вы не можете совместно использовать управляемый объект между потоками/очередями. Остается ли дело с performBlock:? Есть следующее:

[context performBlock:^{
    // fetch request code

    NSArray *results = [context executeFetchRequest:request error:nil];

    dispatch_async(dispatch_get_main_queue(), ^(void) {
        Class *firstObject = [results objectAtIndex:0];
        // do something with firstObject
    });
}];

все еще неприемлемо, так как я делюсь своим массивом результатов/объектами между очередью bg и основной очередью? Должен ли я использовать идентификаторы управляемых объектов для этого?

Ответ 1

При использовании NSPrivateQueueConcurrencyType вам нужно сделать все, что касается этого контекста или любого объекта, принадлежащего этому контексту, внутри метода -performBlock:.

Ваш код выше является незаконным, поскольку вы передаете эти объекты обратно в основную очередь. Однако новый API помогает вам в решении этого: вы создаете один контекст, связанный с основной очередью, т.е. С NSMainQueueConcurrencyType:

// Assume we have these two context (They need to be set up. Assume they are.)
NSManagedObjectContext *mainMOC = [[[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType] autorelease];
NSManagedObjectContext *backgroundMOC = [[[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType] autorelease];

// Now this can safely be called from ANY thread:
[backgroundMOC performBlock:^{
    NSArray *results = [backgroundMOC executeFetchRequest:request error:nil];
    for (NSManagedObject *mo in results) {
        NSManagedObjectID *moid = [mo objectID];
        [mainMOC performBlock:^{
            NSManagedObject *mainMO = [mainMOC objectWithID:moid];
            // Do stuff with 'mainMO'. Be careful NOT to use 'mo'.
        }];
    }
}];

Это становится менее запутанным, если вы переместите внутренний вызов [mainMOC performBlock:] в свой собственный метод. Вы также можете передать массив идентификаторов объектов обратно в контекст основного потока вместо выполнения блока для каждого идентификатора объекта. Это зависит от ваших потребностей.

Ответ 2

Как объясняет Даниэль Эггерт, это определенно все еще так. Исключение составляет NSMainQueueConcurrencyType, где вы также можете безопасно использовать контекст и объекты управляемого объекта в основном потоке (а также из других потоков через механизм executeBlock). Полезность этого нельзя недооценивать!

iOS 5 также представила концепцию родительских контекстов, которая также значительно упрощает фоновые операции и устраняет необходимость беспокоиться об использовании уведомлений для распространения изменений между потоками.

WWDC 2012 видео "Сессия 214 - Основные рекомендации по основным данным" более подробно описывает обе темы и является очень всеобъемлющей. Видео является важным просмотром для тех, кто использует Core Data.