Compreendendo NSRunLoop

108

Alguém pode explicar o que é NSRunLoop? então pelo que eu sei NSRunLoopé algo conectado com NSThreadcerto? Então, suponha que eu crie um Tópico como

NSThread* th=[[NSThread alloc] initWithTarget:self selector:@selector(someMethod) object:nil];
[th start];

-(void) someMethod
{
    NSLog(@"operation");
}

então após este Thread terminar seu trabalho certo? porque usar RunLoopsou onde usar? dos documentos da Apple, eu li algo, mas não está claro para mim, então, explique da forma mais simples possível

taffarel
fonte
Esta questão é muito ampla em escopo. Refine sua pergunta para algo mais específico.
Jody Hagins
3
em primeiro lugar, eu quero saber o que fazer no NSRunLoop genérico e como ele se conectou com Thread
taffarel

Respostas:

211

Um loop de execução é uma abstração que (entre outras coisas) fornece um mecanismo para lidar com as fontes de entrada do sistema (soquetes, portas, arquivos, teclado, mouse, temporizadores, etc).

Cada NSThread tem seu próprio loop de execução, que pode ser acessado por meio do método currentRunLoop.

Em geral, você não precisa acessar o loop de execução diretamente, embora haja alguns componentes (de rede) que podem permitir que você especifique qual loop de execução eles usarão para processamento de E / S.

Um loop de execução para um determinado encadeamento esperará até que uma ou mais de suas fontes de entrada tenham alguns dados ou eventos e, em seguida, disparará o (s) manipulador (es) de entrada apropriado (s) para processar cada fonte de entrada que está "pronta".

Depois de fazer isso, ele retornará ao seu loop, processando a entrada de várias fontes e "dormindo" se não houver trabalho a fazer.

Essa é uma descrição de nível bastante elevado (tentando evitar muitos detalhes).

EDITAR

Uma tentativa de abordar o comentário. Eu o quebrei em pedaços.

  • isso significa que eu só posso acessar / executar o loop dentro do thread, certo?

De fato. NSRunLoop não é seguro para thread e só deve ser acessado a partir do contexto do thread que está executando o loop.

  • existe algum exemplo simples de como adicionar evento para executar o loop?

Se você quiser monitorar uma porta, basta adicionar essa porta ao loop de execução e, em seguida, o loop de execução observará a atividade dessa porta.

- (void)addPort:(NSPort *)aPort forMode:(NSString *)mode

Você também pode adicionar um cronômetro explicitamente com

- (void)addTimer:(NSTimer *)aTimer forMode:(NSString *)mode
  • o que significa que ele retornará ao seu loop?

O loop de execução processará todos os eventos prontos a cada iteração (de acordo com seu modo). Você precisará consultar a documentação para descobrir sobre os modos de execução, pois isso está um pouco além do escopo de uma resposta geral.

  • O loop de execução está inativo quando eu inicio o thread?

Na maioria dos aplicativos, o loop de execução principal será executado automaticamente. No entanto, você é responsável por iniciar o loop de execução e responder aos eventos de entrada dos threads que rodar.

  • é possível adicionar alguns eventos ao loop de execução de Thread fora do thread?

Não tenho certeza do que você quer dizer aqui. Você não adiciona eventos ao loop de execução. Você adiciona fontes de entrada e fontes de cronômetro (do thread que possui o loop de execução). O loop de corrida então observa a atividade deles. Você pode, é claro, fornecer entrada de dados de outros encadeamentos e processos, mas a entrada será processada pelo loop de execução que está monitorando essas fontes no encadeamento que está executando o loop de execução.

  • Isso significa que às vezes posso usar o loop de execução para bloquear o thread por um tempo

De fato. Na verdade, um loop de execução "permanecerá" em um manipulador de eventos até que ele retorne. Você pode ver isso em qualquer aplicativo de forma simples. Instale um manipulador para qualquer ação IO (por exemplo, pressionar o botão) que entra em suspensão. Você bloqueará o loop de execução principal (e toda a IU) até que o método seja concluído.

O mesmo se aplica a qualquer loop de execução.

Eu sugiro que você leia a seguinte documentação sobre loops de execução:

https://developer.apple.com/documentation/foundation/nsrunloop

e como eles são usados ​​nos threads:

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW1

Jody Hagins
fonte
2
isso significa que eu só posso acessar / executar o loop dentro do thread, certo? existe algum exemplo simples de como adicionar evento para executar o loop? o que significa que ele retornará ao seu loop? O loop de execução está inativo quando eu inicio o thread? é possível adicionar alguns eventos ao loop de execução do Thread fora do thread? Isso significa que às vezes posso usar o loop de execução para bloquear o thread por um tempo?
taffarel
"Instale um manipulador para qualquer ação IO (por exemplo, pressionar o botão) que dorme." Quer dizer que se eu continuar segurando meu dedo no botão, ele continuará a bloquear o tópico por um tempo ?!
Mel
Não. O que quero dizer é que o runloop não processa novos eventos até que o manipulador seja concluído. Se você dormir (ou realizar alguma operação que demore muito tempo) no manipulador, o loop de execução será bloqueado até que o manipulador conclua seu trabalho.
Jody Hagins
@taffarel é possível adicionar alguns eventos ao loop de execução do Thread fora do thread? Se isso significa "posso fazer o código rodar no loop de execução de outro thread à vontade", então a resposta é sim. Basta chamar performSelector:onThread:withObject:waitUntilDone:, passando um NSThreadobjeto e seu seletor será agendado no runloop desse thread.
Mecki
12

Os loops de execução são o que separa os aplicativos interativos das ferramentas de linha de comando .

  • As ferramentas de linha de comando são iniciadas com parâmetros, executam seus comandos e saem.
  • Os aplicativos interativos aguardam a entrada do usuário, reagem e, em seguida, retomam a espera.

A partir daqui

Eles permitem que você espere até que o usuário toque e responda de acordo, espere até obter um completeHandler e aplique seus resultados, espere até obter um cronômetro e executar uma função. Se você não tem um runloop, então você não pode ouvir / aguardar os toques do usuário, você não pode esperar até que uma chamada de rede esteja acontecendo, você não pode ser acordado em x minutos a menos que use DispatchSourceTimerouDispatchWorkItem

Também a partir deste comentário :

Threads de segundo plano não têm seus próprios runloops, mas você pode apenas adicionar um. Por exemplo, AFNetworking 2.x fez isso. Foi uma técnica testada e comprovada para NSURLConnection ou NSTimer em threads de fundo, mas não fazemos mais isso sozinhos, pois as APIs mais recentes eliminam a necessidade de fazê-lo. Mas parece que URLSession faz, por exemplo, aqui está uma solicitação simples , executando manipuladores de conclusão [veja o painel esquerdo da imagem] na fila principal, e você pode ver que tem um loop de execução no thread de segundo plano


Especificamente sobre: ​​"Threads de fundo não têm seus próprios runloops". O seguinte cronômetro falha ao disparar por um despacho assíncrono :

class T {
    var timer: Timer?

    func fireWithoutAnyQueue() {
        timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { _ in
            print("without any queue") // success. It's being ran on main thread, since playgrounds begin running from main thread
        })
    }

    func fireFromQueueAsnyc() {
        let queue = DispatchQueue(label: "whatever")
        queue.async {
            self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { (_) in
                print("from a queue — async") // failed to print
            })
        }
    }

    func fireFromQueueSnyc() {
        let queue = DispatchQueue(label: "whatever")
        queue.sync {
            timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { (_) in
                print("from a queue — sync") // success. Weird. Read my possible explanation below
            })
        }
    }

    func fireFromMain() {
        DispatchQueue.main.async {
            self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { (_) in
                print("from main queue — sync") //success
            })
        }
    }
}

Eu acho que a razão do sync bloco também ser executado é:

blocos de sincronização geralmente são executados apenas de dentro da fila de origem . Neste exemplo, a fila de origem é a fila principal, o que quer que seja seja a fila de destino.

Para testar que eu loguei RunLoop.current dentro de cada despacho.

O envio de sincronização tinha o mesmo loop de execução da fila principal. Enquanto o RunLoop dentro do bloco assíncrono era uma instância diferente das outras. Você pode estar pensando como por que RunLoop.currentretorna um valor diferente. Não é compartilhado valor !? Ótima pergunta! Leia mais:

NOTA IMPORTANTE:

A propriedade da classe current NÃO é uma variável global.

Retorna o loop de execução para o segmento atual .

É contextual. É visível apenas dentro do escopo do thread, ou seja , armazenamento local do thread . Para mais informações, veja aqui .

Este é um problema conhecido com temporizadores. Você não tem o mesmo problema se usarDispatchSourceTimer

Mel
fonte
8

RunLoops são como uma caixa onde as coisas simplesmente acontecem.

Basicamente, em um RunLoop, você vai processar alguns eventos e depois retorna. Ou retorne se não processar nenhum evento antes que o tempo limite seja atingido. Você pode dizer que é semelhante a NSURLConnections assíncronos, Processando dados em segundo plano sem interferir no seu loop atual e, ao mesmo tempo, você precisa de dados de forma síncrona. O que pode ser feito com a ajuda do RunLoop que torna o seu assíncrono NSURLConnectione fornece dados na hora da chamada. Você pode usar um RunLoop como este:

NSDate *loopUntil = [NSDate dateWithTimeIntervalSinceNow:0.1];

while (YourBoolFlag && [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate:loopUntil]) {
    loopUntil = [NSDate dateWithTimeIntervalSinceNow:0.1];
}

Neste RunLoop, ele será executado até que você conclua alguns de seus outros trabalhos e defina YourBoolFlag como false .

Da mesma forma, você pode usá-los em threads.

Espero que isso ajude você.

Akshay Sunderwani
fonte
0

Os loops de execução fazem parte da infraestrutura fundamental associada aos threads. Um loop de execução é um loop de processamento de eventos que você usa para agendar o trabalho e coordenar o recebimento de eventos recebidos. O objetivo de um loop de execução é manter seu encadeamento ocupado quando há trabalho a ser feito e colocá-lo em suspensão quando não houver nenhum.

Daqui


O recurso mais importante do CFRunLoop é o CFRunLoopModes. CFRunLoop trabalha com um sistema de “Run Loop Sources”. As fontes são registradas em um loop de execução para um ou vários modos, e o próprio loop de execução é feito para funcionar em um determinado modo. Quando um evento chega a uma fonte, ele só é tratado pelo loop de execução se o modo de origem corresponder ao modo atual do loop de execução.

Daqui

dengApro
fonte