WKWebView appelle évaluerJavaScript dans le thread enfant pour renvoyer des problèmes potentiels de manière synchrone
Fond d'affaires
UIWebView exécute JS pour retourner de manière synchrone et WKWebView exécute JS pour retourner de manière asynchrone.
Ici, le thread principal est bloqué via une boucle infinie pour obtenir l'effet de WKWebView exécutant le retour synchrone JS. En même temps, exécutez manuellement NSRunLoop dans la boucle While pour vous assurer que l'interface n'est pas bloquée.
Dans le code métier, lorsque le code métier qui appelle le fragment de code se trouve dans le rappel de demande de réseau ou scanne le rappel de code QR, il est nécessaire de changer le thread principal du thread enfant. La situation que j'ai rencontrée scannait l'interface du code QR et avait besoin de changer le thread principal.
Exemple de code
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]];
}
});
}
problème apparaît
Mais lorsque le code métier externe utilise dispatch_async et dispatch_get_main_queue pour basculer le thread principal à exécuter, il arrivera qu'évalueJavaScript soit bloqué et que le completionHandler ne puisse pas être appelé.
cause première
L' dispatch_async()
interface fournie par GCD utilise également RunLoop. Lorsque vous appelez dispatch_async(dispatch_get_main_queue(), block)
, libDispatch RunLoop enverra un message au thread principal, RunLoop se réveillera, récupérera le bloc du message et CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE()
exécutera un rappel dans ce bloc. Mais cette logique est limitée à la distribution au thread principal, la distribution à d'autres threads est toujours gérée par libDispatch.
On estime que la evaluateJavaScript
méthode WKWebView completionHandler
est utilisée lorsque l' appel est préparé après l'exécution dispatch_async(dispatch_get_main_queue(), block)
, ce qui entraîne une CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
concurrence entre les files d' attente et finalement un blocage.
Le code de démonstration est le suivant:
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]];
}
});
Mais CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
l'occupation de la file d'attente n'est dispatch_async(dispatch_get_main_queue(), block)
causée que par l'appel de méthode, le code normal du thread principal ne sera pas exécuté dans la file d'attente, il n'y aura donc pas de blocage.
solution
- Code métier
S'il était à l'origine utilisé dans le code métierdispatch_async(dispatch_get_main_queue(), block)
, utilisez plutôt performSelectorOnMainThread.
Cette méthode n'occupe pas laCFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
file d'attente, il n'y a donc pas de problème. - Code système
Si ladispatch_async(dispatch_get_main_queue(), block)
méthode utilisée est du code système, tel que le rappel de code QR décrit dans le contexte commercial ou le rappel de NSNotification , leurs méthodes de rappel sont implicitement appelées, ce qui provoquera également le problème de blocage ci-dessus.
Des situations comme celle-ci ne peuvent pas être résolues pour le moment, et il est recommandé d'utiliser des méthodes asynchrones pour appeler lesevaluateJavaScript
méthodes honnêtement .
Article de référence
Entretien iOS Solution complète 2: Runloop
https://www.jianshu.com/p/37025d0612e8
Logique du principe du mécanisme de fonctionnement de RunLoop et analyse des relations GCD et thread
https://www.jianshu.com/p/efc4dcaf4c05