Подтвердить что ты не робот

Основные данные и потоки /Grand Central Dispatch

Я начинаю с Grand Central Dispatch (GCD) и Core Data, и мне нужна ваша помощь для использования Core Data с CGD, поэтому пользовательский интерфейс не заблокирован, а я добавляю 40 000 записей в Core Data.

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

То, что я не мог сделать, - это собрать кусочки вместе.

Итак, в моем коде мне нужна ваша помощь, как к этому.

У меня есть:

/*some other code*/

for (NSDictionary *memberData in arrayWithResult) {

    //get the Activities for this member
    NSArray *arrayWithMemberActivities = [activitiesDict objectForKey:[memberData objectForKey:@"MemberID"]];

    //create the Member, with the NSSet of Activities
    [Members createMemberWithDataFromServer:memberData
                         andActivitiesArray:arrayWithMemberActivities
                              andStaffArray:nil
                           andContactsArray:nil
                     inManagedObjectContext:self.managedObjectContext];
}

Как я могу преобразовать это, чтобы работать в фоновом режиме, а затем, когда это было сделано, сохранить данные и обновить пользовательский интерфейс, не блокируя пользовательский интерфейс при сохранении 40 000 объектов?

4b9b3361

Ответ 1

Вот хороший пример для вас. Не стесняйтесь возвращаться, если у вас есть вопросы:

self.mainThreadContext... // This is a reference to your main thread context
NSPersistentStoreCoordinator *mainThreadContextStoreCoordinator = [self.mainThreadContext persistentStoreCoordinator];
dispatch_queue_t request_queue = dispatch_queue_create("com.yourapp.DescriptionOfMethod", NULL);
dispatch_async(request_queue, ^{

    // Create a new managed object context
    // Set its persistent store coordinator
    NSManagedObjectContext *newMoc = [[NSManagedObjectContext alloc] init];
    [newMoc setPersistentStoreCoordinator:mainThreadContextStoreCoordinator]];

    // Register for context save changes notification
    NSNotificationCenter *notify = [NSNotificationCenter defaultCenter];
    [notify addObserver:self 
               selector:@selector(mergeChanges:) 
                   name:NSManagedObjectContextDidSaveNotification 
                 object:newMoc];

    // Do the work
    // Your method here
    // Call save on context (this will send a save notification and call the method below)
    BOOL success = [newMoc save:&error];
    if (!success)
        // Deal with error
    [newMoc release];
});
dispatch_release(request_queue);

И в ответ на уведомление о сохранении контекста:

- (void)mergeChanges:(NSNotification*)notification 
{
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.mainThreadContext mergeChangesFromContextDidSaveNotification:notification waitUntilDone:YES];
    });
}

И не забудьте удалить наблюдателя из центра уведомлений, как только закончите с контекстом фонового потока.

[[NSNotificationCenter defaultCenter] removeObserver:self];

Ответ 2

Вот фрагмент, который охватывает GCD и UI в нем простейшие термины. Вы можете заменить doWork кодом, который работает с CoreData.

Что касается безопасности компакт-дисков и потоков, одна из приятных частей GCD - это возможность отключить участки вашего приложения (подсистем) для синхронизации и обеспечить их выполнение в одной очереди. Вы можете выполнить всю работу CoreData в очереди с именем com.yourcompany.appname.dataaccess.

В образце есть кнопка, которая вызывает длительную работу, метку состояния, и я добавил слайдер, чтобы показать, что я могу перемещать ползунок, когда выполняется работа bg.

// on click of button
- (IBAction)doWork:(id)sender
{
    [[self feedbackLabel] setText:@"Working ..."];
    [[self doWorkButton] setEnabled:NO];

    // async queue for bg work
    // main queue for updating ui on main thread
    dispatch_queue_t queue = dispatch_queue_create("com.sample", 0);
    dispatch_queue_t main = dispatch_get_main_queue();

    //  do the long running work in bg async queue
    // within that, call to update UI on main thread.
    dispatch_async(queue, 
                   ^{ 
                       [self performLongRunningWork]; 
                       dispatch_async(main, ^{ [self workDone]; });
                   });

    // release queues created.
    dispatch_release(queue);    
}

- (void)performLongRunningWork
{
    // simulate 5 seconds of work
    // I added a slider to the form - I can slide it back and forth during the 5 sec.
    sleep(5);
}

- (void)workDone
{
    [[self feedbackLabel] setText:@"Done ..."];
    [[self doWorkButton] setEnabled:YES];
}

Ответ 4

Добавление другого источника информации, которую вы можете проверить

ThreadedCoreData

Пример кода библиотеки разработчиков Apple iOS, которые были недавно обновлены (2013-06-09)

Демонстрирует, как использовать базовые данные в многопоточной среде, после первого рекомендованного шаблона, указанного в основных данных Руководство по программированию.

Основываясь на примере SeismicXML, он загружает и анализирует канал RSS из Геологической службы Соединенных Штатов (USGS), которая предоставляет данные о недавние землетрясения во всем мире. Что делает этот образец разным заключается в том, что он постоянно хранит землетрясения с использованием Core Data. Каждый раз вы запускаете приложение, оно загружает новые данные о землетрясениях, анализирует его в NSOperation, которая проверяет дубликаты и хранит вновь созданные землетрясения как управляемые объекты.

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

Ответ 5

Итак, выбранный ответ для этого - это почти два года назад, и есть несколько проблем с ним:

  • Это не ARC-friendly - нужно удалить приглашение на выпуск newMoc - ARC даже не скомпилируется с этим
  • Вы должны делать танец weakSelf/strongSelf внутри блока - иначе вы, вероятно, создаете цикл сохранения в создании наблюдателя. Смотрите документ Apple здесь: http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html
  • @RyanG спросил в комментарии, почему он блокирует. Я предполагаю, что недавно отредактированный метод имеет waitUntilDone: YES - за исключением того, что будет заблокирован основной поток. Вероятно, вы хотите waitUntilDone: НЕТ, но я не знаю, будет ли обновляться пользовательский интерфейс из этих событий изменения, поэтому потребуется тестирование.

- Edit -

Глядя дальше в # 3 - waitUntilDone: ДА не является допустимым методомСогласование для управляемых объектов контекста, так как это работает?

Ответ 6

Намного проще это сделать, чем привязать постоянный координатор хранилища к новому контексту, который также не является потокобезопасным, btw.

NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrency];
[context setParentContext:<main thread context here>];

[context performBlock:^{

    ...
    // Execute all code on current context
    ...

}];

NSError *error = nil;
[context save:&error];
if (!error) {
    [context.parentContext save:&error];
    if (error) {
        NSLog(@"Could not save parent context: %@", error);
    }
}
else {
    NSLog(@"Could not save context: %@", error);
}

Отличное руководство по использованию многоконтекстных данных ядра:

http://www.cocoanetics.com/2012/07/multi-context-coredata/