¿Alternativas a dispatch_get_current_queue () para bloques de finalización en iOS 6?

Tengo un método que acepta un bloque y un bloque de finalización. El primer bloque debe ejecutarse en segundo plano, mientras que el bloque de finalización debe ejecutarse en la cola en que se invocó el método.

Para este último siempre utilicé dispatch_get_current_queue() , pero parece que está obsoleto en iOS 6 o superior. ¿Qué debería usar en su lugar?

El patrón de “ejecutar en cualquier cola en la que estuviera la persona que llama” es atractivo, pero en última instancia no es una gran idea. Esa cola podría ser una cola de baja prioridad, la cola principal o alguna otra cola con propiedades impares.

Mi enfoque favorito para esto es decir “el bloque de finalización se ejecuta en una cola definida de implementación con estas propiedades: x, y, z”, y dejar que el bloque se dirija a una cola en particular si la persona que llama quiere más control que eso. Un conjunto típico de propiedades para especificar sería algo así como “serial, non-reentrant y async con respecto a cualquier otra aplicación: cola visible”.

** EDIT **

Catfish_Man puso un ejemplo en los comentarios a continuación, solo lo estoy agregando a su respuesta.

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

Este es fundamentalmente el enfoque equivocado para la API que está describiendo para tomar. Si una API acepta un bloque y un bloque de finalización para ejecutar, los siguientes hechos deben ser ciertos:

  1. El “bloque para ejecutar” debe ejecutarse en una cola interna, por ejemplo, una cola que es privada para la API y, por lo tanto, está completamente bajo el control de esa API. La única excepción a esto es si la API declara específicamente que el bloque se ejecutará en la cola principal o en una de las colas simultáneas globales.

  2. El bloque de finalización siempre se debe express como una tupla (cola, bloque) a menos que las mismas suposiciones que para el # 1 sean verdaderas, por ejemplo, el bloque de finalización se ejecutará en una cola global conocida. El bloque de finalización debe además enviarse de manera asincrónica en la cola de transferencia.

Estos no son solo puntos estilísticos, son completamente necesarios si su API está a salvo de interlockings u otros comportamientos extremos que, de lo contrario, lo colgarán del árbol más cercano algún día. 🙂

Las otras respuestas son geniales, pero para mí la respuesta es estructural. Tengo un método como este que está en Singleton:

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

que tiene dos dependencias, que son:

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

y

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

De esa manera, centralizo mis llamadas para enviarlas en el otro hilo.

En primer lugar, debe tener cuidado con el uso de dispatch_get_current_queue . Desde el archivo de encabezado:

Recomendado solo para fines de depuración y registro:

El código no debe hacer suposiciones sobre la cola devuelta, a menos que sea una de las colas globales o una cola que el código haya creado. El código no debe suponer que la ejecución síncrona en una cola está a salvo del interlocking si esa cola no es la devuelta por dispatch_get_current_queue ().

Puedes hacer una de estas dos cosas:

  1. Mantenga una referencia a la cola en la que publicó originalmente (si la creó a través de dispatch_queue_create ) y úsela a partir de ese momento.

  2. Use las colas definidas por el sistema a través de dispatch_get_global_queue , y mantenga una pista de la que está usando.

Efectivamente, mientras dependía previamente del sistema para realizar un seguimiento de la cola en la que se encuentra, tendrá que hacerlo usted mismo.

Para aquellos que aún necesitan una comparación en cola, puede comparar las colas por su etiqueta o especificar. Mira esto https://stackoverflow.com/a/23220741/1531141

Apple había dejado de usar dispatch_get_current_queue() , pero dejó un agujero en otro lugar, por lo que todavía podemos obtener la cola de despacho actual:

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

Esto funciona para cola principal al menos. Tenga en cuenta que underlyingQueue propiedad underlyingQueue Queue está disponible desde iOS 8.

Si necesita realizar el bloque de finalización en la cola original, también puede usar OperationQueue directamente, quiero decir sin GCD.

Esta es una respuesta yo también. Entonces hablaré sobre nuestro caso de uso.

Tenemos una capa de servicios y la capa de UI (entre otras capas). La capa de servicios ejecuta tareas en segundo plano. (Tareas de manipulación de datos, tareas de CoreData, llamadas de red, etc.). La capa de servicio tiene un par de colas de operaciones para satisfacer las necesidades de la capa de UI.

La capa de interfaz de usuario se basa en la capa de servicios para hacer su trabajo y luego ejecutar un bloque de finalización de éxito. Este bloque puede tener código UIKit en él. Un caso de uso simple es obtener todos los mensajes del servidor y volver a cargar la vista de colección.

Aquí garantizamos que los bloques que se pasan a la capa de servicios se envían a la cola en la que se invocó el servicio. Como dispatch_get_current_queue es un método obsoleto, usamos NSOperationQueue.currentQueue para obtener la cola actual de la persona que llama. Nota importante sobre esta propiedad.

Llamar a este método desde fuera del contexto de una operación en ejecución generalmente resulta en que no se devuelve nada.

Como siempre invocamos nuestros servicios en una cola conocida (nuestras colas personalizadas y la cola principal) esto nos funciona bien. Tenemos casos donde serviceA puede llamar a serviceB que puede llamar a serviceC. Como controlamos desde dónde se realiza la primera llamada de servicio, sabemos que el rest de los servicios seguirán las mismas reglas.

Así que NSOperationQueue.currentQueue siempre devolverá una de nuestras Colas o la MainQueue.