Основные данные и потоки /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 объектов?
Ответы
Ответ 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];
}
Ответ 3
В этом блоге есть подробное описание основных данных concurrency и пример кода:
http://www.duckrowing.com/2010/03/11/using-core-data-on-multiple-threads/
Ответ 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/