Problèmes potentiels liés à l'appel de WKWebView à evaluerJavaScript d'un retour asynchrone à synchrone

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 evaluateJavaScriptméthode WKWebView completionHandlerest 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_QUEUEconcurrence 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_QUEUEl'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

  1. Code métier
    S'il était à l'origine utilisé dans le code métier dispatch_async(dispatch_get_main_queue(), block), utilisez plutôt performSelectorOnMainThread.
    Cette méthode n'occupe pas la CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUEfile d'attente, il n'y a donc pas de problème.
  2. Code système
    Si la dispatch_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 les evaluateJavaScriptmé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

Je suppose que tu aimes

Origine blog.csdn.net/jhq1990/article/details/113921088
conseillé
Classement