Альтернативы отправки get current queue () для блоков завершения в iOS 6?


У меня есть метод, который принимает блок и блок завершения. Первый блок должен выполняться в фоновом режиме, в то время как блок завершения должен выполняться в любой очереди, в которой был вызван метод.

для последнего я всегда использовал dispatch_get_current_queue(), но похоже, что он устарел в iOS 6 или выше. Что я должен использовать вместо этого?

7 93

7 ответов:

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

мой любимый подход к этому-сказать ,что" блок завершения работает на определенной очереди реализации с этими свойствами: x, y, z", и пусть блок отправляется в определенную очередь, если вызывающий хочет больше контроля, чем это. Типичный набор свойств для указания было бы что-то вроде "последовательный, не реентерабельный и асинхронный по отношению к любой другой очереди видимых приложений".

* * EDIT**

Catfish_Man поставил пример в комментариях ниже, я просто добавляю его к своему ответу.

- (void) aMethodWithCompletionBlock:(dispatch_block_t)completionHandler     
{ 
    dispatch_async(self.workQueue, ^{ 
        [self doSomeWork]; 
        dispatch_async(self.callbackQueue, completionHandler); 
    } 
}

Это принципиально неправильный подход для API, который вы описываете. Если API принимает блок и блок завершения для запуска, следующие факты должны быть истинными:

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

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

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

другие ответы велики, но для меня ответ является структурным. У меня есть такой метод, который находится на синглтоне:

- (void) dispatchOnHighPriorityNonMainQueue:(simplest_block)block forceAsync:(BOOL)forceAsync {
    if (forceAsync || [NSThread isMainThread])
        dispatch_async_on_high_priority_queue(block);
    else
        block();
}

, которая имеет две зависимости, которые являются:

static void dispatch_async_on_high_priority_queue(dispatch_block_t block) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), block);
}

и

typedef void (^simplest_block)(void); // also could use dispatch_block_t

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

вы должны быть осторожны об использовании dispatch_get_current_queue в первую очередь. Из файла заголовка:

рекомендуется только для отладки и ведения журнала:

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

вы могли бы сделать одну из двух вещей:

  1. Сохраните ссылку на очередь, которую вы первоначально разместили (если вы создали ее через dispatch_queue_create), и использовать это с тех пор.

  2. используйте системные очереди через dispatch_get_global_queue, и отслеживать, какой из них вы используете.

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

для тех, кто все еще нуждается в сравнении очереди, вы можете сравнить очереди по их метке или указывает. Проверьте это https://stackoverflow.com/a/23220741/1531141

Apple устарела dispatch_get_current_queue(), но оставил дыру в другом месте, поэтому мы все еще можем получить текущую очередь отправки:

if let currentDispatch = OperationQueue.current?.underlyingQueue {
    print(currentDispatch)
    // Do stuff
}

это работает для основной очереди, по крайней мере. Заметьте, что underlyingQueue свойство доступно с iOS 8.

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

Это мне тоже ответ. Поэтому я расскажу о нашем случае использования.

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

уровень пользовательского интерфейса использует уровень служб для выполнения своей работы, а затем запускает блок успешного завершения. Этот блок может иметь код UIKit в оно. Простой вариант использования-получить все сообщения с сервера и перезагрузить представление коллекции.

здесь мы гарантируем, что блоки, которые передаются на уровень служб, отправляются в очередь, в которой была вызвана служба. Поскольку dispatch_get_current_queue является устаревшим методом, мы используем NSOperationQueue.currentQueue для получения текущей очереди вызывающего абонента. Обратите внимание на это свойство.

вызов этого метода из вне контекста выполняется операция как правило, результаты в ноль возвращается.

поскольку мы всегда вызываем наши службы в известной очереди (наши пользовательские очереди и основная очередь), это хорошо работает для нас. У нас есть случаи, когда serviceA может вызвать serviceB, который может вызвать serviceC. Поскольку мы контролируем, откуда выполняется первый вызов службы, мы знаем, что остальные службы будут следовать тем же правилам.

Так NSOperationQueue.currentQueue всегда возвращает одну из наших очередей или Главное.