Alternativas para dispatch_get_current_queue () para blocos de conclusão no iOS 6?

Eu tenho um método que aceita um bloco e um bloco de conclusão. O primeiro bloco deve ser executado em segundo plano, enquanto o bloco de conclusão deve ser executado em qualquer fila em que o método foi chamado.

Para o último, sempre usei dispatch_get_current_queue() , mas parece que ele está obsoleto no iOS 6 ou superior. O que devo usar em vez disso?

O padrão de “rodar em qualquer fila em que o chamador estava” é atraente, mas no final não é uma ótima idéia. Essa fila pode ser uma fila de baixa prioridade, a fila principal ou alguma outra fila com propriedades ímpares.

Minha abordagem favorita para isso é dizer “o bloco de conclusão é executado em uma fila definida pela implementação com estas propriedades: x, y, z” e deixar o bloco despachar para uma fila específica se o chamador quiser mais controle do que isso. Um conjunto típico de propriedades para especificar seria algo como “serial, não reentrante e asynchronous com relação a qualquer outra fila visível ao aplicativo”.

** EDITAR **

Catfish_Man colocou um exemplo nos comentários abaixo, só estou adicionando a resposta dele.

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

Esta é fundamentalmente a abordagem errada para a API que você está descrevendo. Se uma API aceitar um bloco e um bloco de conclusão para ser executado, os seguintes fatos precisam ser verdadeiros:

  1. O “bloco a ser executado” deve ser executado em uma fila interna, por exemplo, uma fila privada para a API e, portanto, totalmente sob o controle dessa API. A única exceção a isso é se a API declara especificamente que o bloco será executado na fila principal ou em uma das filas simultâneas globais.

  2. O bloco de conclusão deve ser sempre expresso como uma tupla (fila, bloco), a menos que as mesmas suposições que para o número 1 sejam verdadeiras, por exemplo, o bloco de conclusão será executado em uma fila global conhecida. O bloco de conclusão também deve ser enviado como asynchronous na fila passada.

Estes não são apenas pontos estilísticos, eles são totalmente necessários se a sua API estiver segura contra deadlocks ou outro comportamento extremo que, de outra forma, irá pendurá-lo da tree mais próxima algum dia. 🙂

As outras respostas são ótimas, mas para mim a resposta é estrutural. Eu tenho um método como este que está em um Singleton:

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

que tem duas dependencies, que são:

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

e

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

Dessa forma, centralizo minhas chamadas para envio no outro segmento.

Você deve ter cuidado com o uso de dispatch_get_current_queue em primeiro lugar. Do arquivo de header:

Recomendado apenas para fins de debugging e registro:

O código não deve fazer suposições sobre a fila retornada, a menos que seja uma das filas globais ou uma fila que o código tenha criado. O código não deve presumir que a execução síncrona em uma fila esteja segura contra o conflito se essa fila não for aquela retornada por dispatch_get_current_queue ().

Você poderia fazer uma das duas coisas:

  1. Mantenha uma referência à fila na qual você originalmente postou (se você a criou via dispatch_queue_create ), e use isso daí em diante.

  2. Use filas definidas pelo sistema via dispatch_get_global_queue e mantenha uma faixa de qual delas você está usando.

Efetivamente enquanto anteriormente confiamos no sistema para manter o controle da fila em que você está, você terá que fazer isso sozinho.

Para aqueles que ainda precisam compará-los na fila, você pode comparar as filas pelo label ou especificar. Marque esta https://stackoverflow.com/a/23220741/1531141

A Apple tinha preterido dispatch_get_current_queue() , mas deixou um buraco em outro lugar, então ainda podemos obter a fila de envio atual:

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

Isso funciona para a fila principal, pelo menos. Observe que underlyingQueue propriedade underlyingQueue está disponível desde o iOS 8.

Se você precisar executar o bloco de conclusão na fila original, também poderá usar o OperationQueue diretamente, quero dizer, sem o GCD.

Esta é uma resposta para mim também. Então, falarei sobre nosso caso de uso.

Temos uma camada de serviços e a camada de interface do usuário (entre outras camadas). A camada de serviços executa tarefas em segundo plano. (Tarefas de manipulação de dados, tarefas CoreData, chamadas de rede etc). A camada de serviço tem algumas filas de operação para satisfazer as necessidades da camada da interface do usuário.

A camada de interface do usuário depende da camada de serviços para fazer seu trabalho e, em seguida, executar um bloco de conclusão de sucesso. Este bloco pode ter o código UIKit nele. Um caso de uso simples é obter todas as mensagens do servidor e recarregar a visualização de coleta.

Aqui garantimos que os blocos que são passados ​​para a camada de serviços são despachados na fila em que o serviço foi invocado. Como dispatch_get_current_queue é um método obsoleto, usamos NSOperationQueue.currentQueue para obter a fila atual do chamador. Nota importante sobre esta propriedade.

Chamar esse método de fora do contexto de uma operação em execução geralmente resulta em retorno nulo.

Como sempre invocamos nossos serviços em uma fila conhecida (nossas filas personalizadas e fila principal), isso funciona bem para nós. Nós temos casos em que serviceA pode chamar serviceB, que pode chamar serviceC. Como controlamos de onde a primeira chamada de serviço está sendo feita, sabemos que o restante dos serviços seguirá as mesmas regras.

Portanto, NSOperationQueue.currentQueue sempre retornará uma de nossas Filas ou o MainQueue.