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

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

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

Ответ 1

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

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

** РЕДАКТИРОВАТЬ **

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

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

Ответ 2

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

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

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

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

Ответ 3

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

- (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

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

Ответ 4

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

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

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

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

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

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

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

Ответ 5

Для тех, кто по-прежнему нуждается в сравнении в очереди, вы можете сравнивать очереди по их метке или специфицировать. Проверьте fooobar.com/questions/17449/...

Ответ 6

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

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

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

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

Ответ 7

Это тоже ответ. Поэтому я расскажу о нашем прецеденте.

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

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

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

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

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

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