1

Итак, после изучения блоков завершения в то время, мне нравится использовать блоки завершения. Мне нравится закрытие, и мне нравится возможность передавать практически все, что захочу.о enqueuing с использованием блоков завершения

Как кто-то, кто является новичком в программировании ниток, я держался подальше от GCD и NSOperation, но в последнее время мне пришлось программировать асинхронное обновление данных Core Data, и я начинаю сомневаться в моем «завершении» блокировать все время ".

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

То, что я закодированы это по существу в основном только функция с блоком завершения в нем, что есть вызов себе в конце блока, как это:

(void)uploadAllAsynchronously { 
    ... // First figure out what to upload based on core data 
    // Here comes the completion block in question 
    void(^blk)(BOOL) = ^(BOOL)uploadSuccess { 
    ... // if upload successful, update core data to mark what has been uploaded 
    [self uploadAllAsynchronously]; // Recursively calls the function that contains this block. I actually have a weak self, or if that fails to break a retain cycle, I should be able to pass in a NSManagedObjectContext as an argument. 
    } 
    [NSURLConnection sendAsynchronousRequest:... queue:... completionHandler:blk]; 


} 

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

Заранее спасибо.

+0

Имейте в виду, что обработчики завершения обычно не работают на фоновом потоке. Обычно (включая '[NSURLConnection sendAsynchronousRequest: queue: completionHandler:]') блок завершения выполняется в основном потоке. Поэтому вам не нужно беспокоиться о безопасности потоков. Если основной поток занят, когда фоновый поток завершает загрузку, он будет ждать, пока основной поток не будет простаивать до выполнения блока завершения. –

+0

Я узнал, что есть один случай, когда вложенные блоки завершения будут проблематичными и могут вызвать взаимоблокировки: http://www.cocoawithlove.com/2010/06/avoiding-deadlocks-and-latency-in.html – Victor

ответ

1

Вашего блока неправильного типа.

Как the documentation для

+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse *, NSData *, NSError *))handler 

показывает, тип заканчивания сажу

void (^) (NSURLResponse *, NSData *, NSError *) 

не

void (^) (BOOL) 

Вы должны изменить blk быть что-то вроде

void (^blk) (NSURLResponse *, NSData *, NSError *) =^(NSURLResponse *response, NSData *data, NSError *error) { 
    //... 
}; 

Было бы более модно написать

[NSURLConnection sendAsynchronousRequest:theRequest queue:theQueue completionHandler:^ (NSURLResponse *response, NSData *data, NSError *error) { 
    //... 
}]; 

с блоком завершения в линии с методом.


Что касается вопроса о совершении операций на вашем NSManagedObjectContext в обработчике завершения: Это нормально, так долго, как NSOperationQueue передается sendAsynchronousRequest:queue:completionHandler: такая же, как та, в которой создается контекст управляемого объекта. Но, как the documentation for NSManagedObjectContext состояний

Ядро данные используют поток (или сериализованные очереди) конфайнмент для защиты управляемых объектов и управляемых контекстов объектов (см «Параллелизм с Core Data»).Следствием этого является то, что контекст предполагает, что владелец по умолчанию является потоком или очередью, которая его назначила, - это определяется потоком, который вызывает его метод init. Поэтому вы не должны инициализировать контекст в одном потоке, а затем передавать его в другой поток. Вместо этого вы должны передать ссылку на постоянный координатор хранилища, а получающий поток/очередь создать новый контекст, полученный из этого.

Если очередь, что вы передаете не один, на котором вы создали контекст управляемого объекта, необходимо выполнить одно из следующих действий

  1. Зова -[NSOperationQueue addOperation:] по очереди, где контекст управляемого объекта был создано.

  2. Создайте второй контекст управляемого объекта (с тем же постоянным координатором хранилища) в очереди, в которой происходят операции с основными данными.

  3. Создайте второй контекст управляемого объекта и второй постоянный координатор хранилища в очереди, в которой происходят операции с основными данными.

  4. Использовать блокировку.

Документация по Concurrency with Core Data становится ясно, что вы должны использовать либо нить конфайнмента (варианты 1-3 выше) или использовать замок (вариант 4 выше).

Это то, что документы должны сказать о заимствовании замков:

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

Это то, что документы должны сказать о том, а не только за нить удалось контексты объектов, но и для каждого потока постоянного хранилища координаторов:

Этот подход обеспечивает большую параллельности за счет большей сложность (особенно, если вам необходимо обмениваться изменениями между различными контекстами) и увеличение использования памяти.

+0

Мое главное с документацией, таким образом: в видеоролике WWDC 2012 Session 214 это выглядит так: «Поэтому вы не должны инициализировать контекст в одном потоке, а затем передавать его в другой поток» больше не применяется в каждом контексте (если это один из MainQueue или PrivateQueue) фактически управлял своей собственной очередью, независимо от того, какой поток она есть. Я не знаю, видели ли вы это видео, но если у вас есть, правильно ли я интерпретирую ведущего в видео? – Victor

+0

У меня нет доступа к этому видео WWDC на данный момент, но в документации по [Параллелию в основных данных] (https://developer.apple.com/library/mac/# documentation/Cocoa/Conceptual/CoreData/Articles/cdConcurrency.html # // apple_ref/doc/uid/TP40003385) довольно ясно, что если вы используете несколько потоков/очередей, вы должны либо использовать поток (имеют контексты управляемых объектов с потоками (и потенциальные постоянные координаторы хранилища на основе потоков)) или используют блокировку. (См. Мое редактирование.) –

+0

Да, это то, о чем я смущен - документация просто не согласуется с моей интерпретацией, как в видео WWDC. Мое впечатление от видео, которое я предполагаю более поздним, говорит, что теперь основные данные заботятся о своих собственных очередях, поэтому, если это абсолютно необходимо, не используйте GCD для вызовов основных данных. Или я думаю - я новичок, когда дело доходит до потоков в любой операционной системе. – Victor

1

да этот код должен работать .. Примечание: Я бы переименовать метод тогда .. uploadIfNeeded может быть - потому что оно не всегда слепо загружать материал ...

Смежные вопросы