Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Многопоточная Core Data

Anna | 2.07.2014 | нет комментариев
Как вестимо, Core Data является сильным Apple фреймворком для управления объектным графом. На Прогре много статей о Core Data, тем не менее, многопоточность освещена довольно слабо, а, как мне кажется, вопросом о том как верно ее реализовать, задавался примерно всякий.

Всеобщие расположения

Если лаконично, то стек Core Data состоит из нескольких основных частей.

1) NSPersistentStore, которым может выступать бинарный файл, XML, SQLite файл.
2) NSManagedObjectModel, которая является скомпилированной бинарной версией модели данных.
3) NSPersistentStoreCoordinator, занимается загрузкой данных из NSPersistentStore и NSManagedObjectModel, сохранением и кешированием.
4) NSManagedObjectContext, загрузка данных из NSPersistentStore в память, операции с экземплярами.
5) NSManagedObject — объект модели данных.

Стержневой, на мой взор, неприятной спецификой каждого этого Дива является то, что NSManagedObjectContext не thread-safe.

Инициализация стека

При крупных размерах БД, при миграциях инициализация стека на основном потоке может занимать больше 30 секунд. Это приведет к тому, что система легко убьет приложение. Выход есть, инициализировать стек в ином потоке.

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // Создание NSManagedObjectModel
        NSURL *modelURL = [[NSBundle mainBundle] URLForResource:kModelFileName withExtension:@"momd"];
        NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

        // Создаем NSPersistentStoreCoordinator
        NSPersistentStoreCoordinator *psc =  [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];

        // Добавляем к NSPersistentStoreCoordinator хранилище, именно на этой операции приложение может висеть дюже длинно
        NSURL *doсURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
                                                                     inDomains:NSUserDomainMask] lastObject];
        NSURL *storeURL = [doсURL URLByAppendingPathComponent:@"CoreData.sqlite"];
        NSError *error = nil;
        NSPersistentStore *store =  [psc addPersistentStoreWithType:NSSQLiteStoreType
                                  configuration:nil
                                            URL:storeURL
                                        options:nil
                                          error:&error];

       // Создание контекстов

});

Выходит, наше приложение было запущено, юзер не получил лагов UI, все довольны. Следуем дальше.

Создание основных контекстов

Как я писал выше, NSManagedObjectContext не thread-safe. Следственно основной контекст приложения целесообразно удерживать именно на основном потоке. Но в таком случае будет тормозить UI при сохранении этого самого контекста, что же делать? А вот что, в iOS 6 возникли типы NSManagedObjectContext.

1) NSMainQueueConcurrencyType – доступен экстраординарно с основного потока.
2) NSPrivateQueueConcurrencyType – работает на фоновом потоке.
3) NSConfinementConcurrencyType – работает на том потоке, на котором сотворили.

А также возникла вероятность создавать дочерние контексты. Дочерний контекст при сохранении пушит все свои метаморфозы родителю. Соответственно, сейчас возникла вероятность обустроить свой администратор по работе с CoreData дальнейшим образом.

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

_daddyManagedObjectContext = [[NSManagedObjectContext alloc]  initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    [_daddyManagedObjectContext setPersistentStoreCoordinator:psc];
    // Дальше в основном потоке инициализируем main-thread context, он будет доступен пользователям
    dispatch_async(dispatch_get_main_queue(), ^{
        _defaultManagedObjectContext = [[NSManagedObjectContext alloc]  initWithConcurrencyType:NSMainQueueConcurrencyType];
        // Добавляем наш приватный контекст отцом, Дабы дочь сумела пушить все метаморфозы
        [_defaultManagedObjectContext setParentContext:_daddyManagedObjectContext];
    });

На этом месте инициализация нашего администратора по работе с Core Data заканчивается, сейчас у нас есть спрятанный от любознательных глаз папа-контекст, а также дочь на основном потоке.

Создание дочерних контекстов

Как дозволено легко додуматься, при работе на бекграунд-потоках мы можем создавать свои контексты, легко добавляя наш, сделанный выше, дочерний контекст в качестве прародителя.

- (NSManagedObjectContext *)getContextForBGTask {
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    [context setParentContext:_defaultManagedObjectContext];
    return context;
}

Такой контекст при сохранении неизменно будет сберегать метаморфозы в своего родителя. Таким образом у нас неизменно будет самая актуальная информация в _defaultManagedObjectContext (тот, что пушит метаморфозы в реального родителя).

Сохранение контекстов

Осталось самое основное — сохранение. К контекстам, которые живут на бекграунд-потоках обращаться дозволено только через performBlock: и performBlockAndWait:. Следственно сохранение бекграунд потока будет выглядеть дальнейшим образом.

- (void)saveContextForBGTask:(NSManagedObjectContext *)bgTaskContext {
    if (bgTaskContext.hasChanges) {
        [bgTaskContext performBlockAndWait:^{
            NSError *error = nil;
            [backgroundTaskContext save:&error];
        }];
       // Save default context
    }
}

Позже сохранения дочернего контекста нужно сберечь родительский.

- (void)saveDefaultContext:(BOOL)wait {
    if (_defaultManagedObjectContext.hasChanges) {
        [_defaultManagedObjectContext performBlockAndWait:^{
            NSError *error = nil;
            [_defaultManagedObjectContext save:&error];
        }];
    }

    // А позже сохранения _defaultManagedObjectContext нужно сберечь его родителя, то есть _daddyManagedObjectContext
    void (^saveDaddyContext) (void) = ^{
        NSError *error = nil;
        [_daddyManagedObjectContext save:&error];
    };
    if ([_daddyManagedObjectContext hasChanges]) {
        if (wait)
            [_daddyManagedObjectContext performBlockAndWait:saveDaddyContext];
        else 
            [_daddyManagedObjectContext performBlock:saveDaddyContext];
    }
}

На этом все. Спасибо за внимание.

 

Источник: programmingmaster.ru

 

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB