Problemas potenciais de WKWebView chamando evaluateJavaScript de retorno assíncrono para síncrono

WKWebView chama evaluateJavaScript no thread filho para retornar problemas potenciais de forma síncrona

Experiência de negócios

UIWebView executa JS para retornar de forma síncrona e WKWebView executa JS para retornar de forma assíncrona.
Aqui, o thread principal é bloqueado por meio de um loop infinito para obter o efeito de WKWebView executando o retorno síncrono JS. Ao mesmo tempo, execute NSRunLoop manualmente no loop While para garantir que a interface não seja travada.
No código de negócios, quando o código de negócios que chama o fragmento de código está no retorno de chamada de solicitação de rede ou escaneia o retorno de chamada do código QR, é necessário alternar o encadeamento principal do encadeamento filho. A situação que encontrei estava digitalizando a interface do código QR e precisava mudar o thread principal.

Código de amostra

WKWebView *sampleWebView;
-(void)errorDemo {
    dispatch_async(dispatch_get_main_queue(), ^{
        sampleWebView = [[WKWebView alloc] initWithFrame:CGRectZero];
        __block BOOL finished = NO;
        [sampleWebView evaluateJavaScript:@"" completionHandler:^(id result, NSError *error) {
            finished = YES; // 该行代码未执行
        }];
        while (!finished) {
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
        }
    });
}

problema aparece

Mas quando o código de negócios externo usa dispatch_async e dispatch_get_main_queue para alternar o thread principal para execução, acontecerá que evaluateJavaScript seja bloqueado e o completeHandler não possa ser chamado.

causa raiz

A dispatch_async()interface fornecida pelo GCD também usa RunLoop. Quando você chama dispatch_async(dispatch_get_main_queue(), block), libDispatch RunLoop enviará uma mensagem para o thread principal, RunLoop será ativado e obterá o bloco da mensagem e a CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE()execução de retorno de chamada neste bloco. Mas essa lógica é limitada ao despacho para o encadeamento principal, o despacho para outros encadeamentos ainda é tratado por libDispatch.

Estima-se que o evaluateJavaScriptmétodo WKWebView completionHandlerseja usado quando a chamada for preparada após a execução dispatch_async(dispatch_get_main_queue(), block), o que leva à CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUEcompetição de filas e, finalmente, a um impasse.

O código de demonstração é o seguinte:

    dispatch_async(dispatch_get_main_queue(), ^{
        __block BOOL finished = NO;
        NSLog(@"evaluateJavaScript");
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            // evaluateJavaScript内部代码
            // ...
            // 调用completionHandler
            dispatch_async(dispatch_get_main_queue(), ^{
                finished = YES; // 该行代码未执行
                NSLog(@"completionHandler");
            });
        });
        while (!finished) {
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
        }
    });

Mas CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUEa ocupação da fila é dispatch_async(dispatch_get_main_queue(), block)causada apenas pela chamada do método, o código da thread principal normal não será executado na fila, portanto, não haverá conflito.

solução

  1. Código comercial
    Se ele foi originalmente usado no código comercial dispatch_async(dispatch_get_main_queue(), block), use performSelectorOnMainThread.
    Este método não ocupa a CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUEfila, então não há problema.
  2. Código do sistema
    Se o dispatch_async(dispatch_get_main_queue(), block)método usado for o código do sistema, como o retorno de chamada do código QR descrito no plano de fundo do negócio ou o retorno de chamada de NSNotification , seus métodos de retorno de chamada são implicitamente chamados, o que também causará o problema de deadlock acima.
    Situações como essa não podem ser resolvidas por enquanto, e é recomendado usar métodos assíncronos para chamar evaluateJavaScriptmétodos honestamente .

Artigo de referência

Entrevista iOS Solução completa 2: Runloop
https://www.jianshu.com/p/37025d0612e8
Lógica do mecanismo operacional RunLoop e GCD e análise de relacionamento de encadeamento
https://www.jianshu.com/p/efc4dcaf4c05

Acho que você gosta

Origin blog.csdn.net/jhq1990/article/details/113921088
Recomendado
Clasificación