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 evaluateJavaScript
método WKWebView completionHandler
seja 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_QUEUE
competiçã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_QUEUE
a 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
- Código comercial
Se ele foi originalmente usado no código comercialdispatch_async(dispatch_get_main_queue(), block)
, use performSelectorOnMainThread.
Este método não ocupa aCFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
fila, então não há problema. - Código do sistema
Se odispatch_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 chamarevaluateJavaScript
mé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