NSDefaultRunLoopMode vs NSRunLoopCommonModes

114

Sempre que eu tento fazer download de um grande por trás do arquivo UIScrollView, MPMapViewou algo assim, o processo de download fica parado assim que a tela de toque do iPhone. Felizmente, uma postagem incrível de Jörn sugere uma opção alternativa, usando NSRunLoopCommonModespara conexão.

Isso me permite ver os detalhes dos dois modos, NSDefaultRunLoopMode e NSRunLoopCommonModes, mas o documento da apple não explica gentilmente, além de dizer

NSDefaultRunLoopMode

O modo para lidar com fontes de entrada diferentes de objetos NSConnection. Este é o modo de loop de execução mais comumente usado.

NSRunLoopCommonModes

Os objetos adicionados a um loop de execução usando este valor como o modo são monitorados por todos os modos de loop de execução que foram declarados como um membro do conjunto de modos "comuns"; consulte a descrição de CFRunLoopAddCommonMode para obter detalhes.

CFRunLoopAddCommonMode

Fontes, temporizadores e observadores são registrados em um ou mais modos de loop de execução e só são executados quando o loop de execução está sendo executado em um desses modos. Os modos comuns são um conjunto de modos de loop de execução para os quais você pode definir um conjunto de fontes, temporizadores e observadores compartilhados por esses modos. Em vez de registrar uma fonte, por exemplo, para cada modo de loop de execução específico, você pode registrá-lo uma vez no pseudo-modo comum do loop de execução e será registrado automaticamente em cada modo de loop de execução no modo comum definido. Da mesma forma, quando um modo é adicionado ao conjunto de modos comuns, quaisquer fontes, temporizadores ou observadores já registrados no pseudo-modo comum são adicionados ao modo comum recém-adicionado.

Alguém pode explicar os dois em linguagem humana?

Stkim1
fonte

Respostas:

204

Um loop de execução é um mecanismo que permite ao sistema despertar threads em espera para que possam gerenciar eventos assíncronos. Normalmente, quando você executa um thread (com exceção do thread principal), há uma opção para iniciar o thread em um loop de execução ou não. Se o thread executa alguma classificação ou operação de longa duração sem interação com eventos externos e sem temporizadores, você não precisa de um loop de execução, mas se seu thread precisa responder a eventos de entrada, deve ser anexado a um loop de execução para desperte o thread quando novos eventos chegarem. Este é o caso deNSURLConnection threads geradas, uma vez que elas só despertam na entrada de eventos (da rede).

Cada thread pode ser associado a vários loops de execução ou pode ser associado a um loop de execução específico que pode ser configurado para funcionar em diferentes modos. Um "modo de loop de execução" é uma convenção usada pelo sistema operacional para estabelecer algumas regras de quando entregar determinados eventos ou coletá-los para entrega posterior.

Normalmente, todos os loops de execução são configurados para o "modo padrão", que estabelece uma maneira padrão de gerenciar eventos de entrada. Por exemplo: assim que ocorre um evento de arrastar do mouse (Mac OS) ou toque (no iOS), o modo para este loop de execução é definido como rastreamento de eventos; isso significa que o encadeamento não será ativado em novos eventos de rede, mas esses eventos serão entregues mais tarde, quando o evento de entrada do usuário terminar e o loop de execução definido para o modo padrão novamente; obviamente, essa é uma escolha feita pelos arquitetos do sistema operacional para dar prioridade aos eventos do usuário em vez dos eventos de segundo plano.

Se você decidir alterar o modo de loop de execução de seu NSURLConnectionencadeamento, usando scheduleInRunLoop:forModes:, você poderá atribuir o encadeamento a um modo de loop de execução especial , em vez do loop de execução padrão específico. O pseudo-modo especial chamado NSRunLoopCommonModesé usado por muitas fontes de entrada, incluindo rastreamento de eventos. Por exemplo, atribuir NSURLConnectiona instância de ao modo comum significa associar seus eventos ao "modo de rastreamento" além do "modo padrão". Uma vantagem / desvantagem de associar encadeamentos NSRunLoopCommonModesé que o encadeamento não será bloqueado por eventos de toque.

Novos modos podem ser adicionados aos modos comuns, mas esta é uma operação de baixo nível.

Eu gostaria de encerrar adicionando algumas notas:

  • Normalmente, precisamos usar um conjunto de imagens ou miniaturas baixadas da rede com uma visualização de tabela. Podemos pensar que baixar essas imagens da rede enquanto a exibição da tabela está rolando pode melhorar a experiência do usuário (já que podemos ver as imagens durante a rolagem), mas isso não é vantajoso, pois a fluidez da rolagem pode sofrer muito. Neste exemplo, com NSURLConnectionum loop de execução não deve ser usado; seria melhor usar os UIScrollViewmétodos delegados para detectar quando a rolagem é encerrada e, em seguida, atualizar a tabela e baixar novos itens da rede;

  • Você pode considerar o uso de GCD, que o ajudará a "proteger" seu código de problemas de gerenciamento de loop de execução. No exemplo acima, você pode considerar adicionar suas solicitações de rede a uma fila serial personalizada.

viggio24
fonte
9
Caro Viggio24, muito obrigado por esta explicação limpa e precisa. Gostaria de pedir à Apple para incluir seu comentário em seu guia de API. ;)
Stkim1
7
a resposta de viggio24 é perfeita. Para os interessados ​​em, gostaria de salientar que a Sessão 208 (Aplicativos de rede para iPhone OS, Parte 2) do WWDC 2010 contém uma introdução sobre loops de execução. Se você estiver interessado, dê uma olhada. Espero que ajude.
Lorenzo B
19
Apenas uma observação para mim: NSRunLoopCommonModespermite evento temporizador enquanto rola para dentro UIScrollView. NSDefaultRunLoopModeevitar cronômetro durante a rolagem.
eonil
2
Achei muito interessante o comentário sobre a atualização do scroll view, pois menciona um tema muito desafiador. Apenas para adicionar mais detalhes sobre isso: Quando você define um modo para um NSURLConnection, isso afeta apenas a execução dos callbacks delegados. Eu entendo que atualizar o scrollView aqui pode resultar em um problema de desempenho, mas por que isso está acontecendo? Se a resposta for que a imagem deve ser carregada na memória, você pode fazer isso escrevendo em um contexto gráfico no fundo e atualizar sua camada de thread principal de visualização depois de fazer isso. isso soa razoável?
nebillo