31 votes

Comment arrêter en toute sécurité un UIWebView en cours de chargement dans viewWillDisappear ?

J'ai une vue contenant un UIWebView qui charge une carte google (donc beaucoup de javascript etc). Le problème que j'ai est que si l'utilisateur appuie sur le bouton 'back' de la barre de navigation avant que la vue web n'ait fini de se charger, il n'est pas clair pour moi comment dire à la vue web d'arrêter de se charger et ensuite de la libérer, sans avoir des messages envoyés à l'instance désallouée. Je ne suis pas non plus certain qu'une vue web apprécie que son conteneur disparaisse avant la fin de son chargement (mais je n'ai pas le choix si l'utilisateur appuie sur le bouton "retour" avant que le chargement ne soit terminé).

Dans mon gestionnaire viewWillDisappear, j'ai ceci

map.delegate=nil;
[self.map stopLoading];

cela semble gérer la plupart des cas sans problème, puisque le fait de mettre le délégué à zéro l'empêche d'envoyer le didFailLoadWithError à mon contrôleur de vue. Cependant, si je libère la vue web dans la méthode dealloc de ma vue, il arrive (par intermittence) que je reçoive toujours un message envoyé à l'instance deallocated, qui semble être lié au javascript exécuté dans la page actuelle, par exemple :

-[UIWebView webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:]: message sent to deallocated instance 0x4469ee0

Si je ne libère pas le webview, je n'obtiens pas ces messages, mais je suppose que je libère alors le webview.

Si je n'envoie pas le message "stopLoading" et que je libère simplement le webview dans viewWillDisappear, je vois des messages comme celui-ci :

/SourceCache/WebCore/WebCore-351.9.42/wak/WKWindow.c:250 WKWindowIsSuspendedWindow:  NULL window.

Peut-être lié à cela, j'ai parfois (encore une fois de manière totalement intermittente) un heisenbug hideux où le fait de cliquer sur le bouton retour de la barre de navigation d'une autre vue fait apparaître le titre, mais pas la vue. En d'autres termes, je me retrouve avec le titre de la vue n sur la pile, mais la vue affichée est toujours la vue n+1 (le résultat est que vous êtes piégé sur cet écran et ne pouvez pas revenir à la vue racine - vous pouvez aller dans l'autre sens, c'est-à-dire pousser plus de vues et revenir à la vue qui ne s'est pas affichée correctement, mais pas à la vue racine. La seule façon de s'en sortir est de quitter l'application). À d'autres moments, la même séquence de poussées et de projections sur les mêmes vues fonctionne bien.

Celui-là en particulier me rend fou. Je pense qu'il peut être lié au fait que la vue disparaît avant que la vue web ne soit chargée, c'est-à-dire que dans ce cas, je soupçonne qu'il peut gribouiller sur la mémoire et confondre la pile de vues. Ou bien, cela pourrait n'avoir aucun rapport et être un bogue ailleurs (je n'ai jamais été capable de le reproduire en mode de construction de débogage, cela ne se produit qu'avec les paramètres de construction de version lorsque je ne peux pas le regarder avec gdb :-). D'après mes tests de débogage, je ne pense pas que je libère trop de choses. Et je ne semble pouvoir le déclencher que si, à un moment donné, j'ai touché la vue qui contient la vue Web, et que cela ne se produit pas immédiatement après.

53voto

rpetrich Points 25769

Une variation de ceci devrait résoudre les problèmes de fuites et de zombies :

- (void)loadRequest:(NSURLRequest *)request
{
    [self retain];
    if ([webView isLoading])
        [webView stopLoading];
    [webView loadRequest:request];
    [self release];
}
- (void)webViewDidStartLoad:(UIWebView *)webView
{
    [self retain];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    [self release];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
    [self release];
}

- (void)viewWillDisappear
{
    if ([webView isLoading])
        [webView stopLoading];
}

- (void)dealloc
{
    [webView setDelegate:nil];
    [webView release];
    [super dealloc];
}

1voto

Le bogue de UINavigationController que vous décrivez dans la deuxième partie de votre message pourrait être lié à votre gestion des avertissements de mémoire. J'ai connu ce phénomène et j'ai pu le reproduire sur la vue n de la pile en simulant un avertissement de mémoire pendant la visualisation de la vue (n+1) de la pile.

UIWebView est un mangeur de mémoire, donc obtenir des avertissements de mémoire ne serait pas surprenant quand il est utilisé comme partie d'une hiérarchie de vue.

1voto

dieselmcfadden Points 173

Il y a plusieurs façons de le faire, mais ça devrait marcher. Vous voulez le message didFailLoadWithError, c'est ce qui vous indique qu'il s'est arrêté.

Définir un indicateur isLeaving=YES ; Envoyer au Webview un stopLoading.

Dans didFailLoadWithError :, vérifiez l'erreur que vous obtenez lorsque le webview s'arrête :

si ((thiserror.code == NSURLErrorCancelled) && (isLeaving==YES)) {

[otherClass performSelector:@selector(shootWebview) withObject:nil withDelay:0]

}

libérer le webView dans shootWebview :


variations : si vous voulez être cavalier, vous pouvez faire le performSelector:withObject:withDelay : avec un délai de [fillintheblank], appelez-le 10-30 secondes sans la vérification et vous vous en sortirez presque certainement, bien que je ne le recommande pas.

Vous pouvez faire en sorte que le didFailLoadWithError définisse un indicateur et le nettoie ailleurs.

ou mon préféré, peut-être que vous n'avez pas besoin de tout désallouer quand vous partez. Vous n'afficherez plus jamais ce conteneur de vues ? Pourquoi ne pas le garder et le réutiliser ?

Votre débogage étant différent du problème de la version, vous pourriez vouloir vérifier votre configuration pour vous assurer qu'elle est exactement la même. La prime était sur la partie reproductible de la question, n'est-ce pas ? ;-).

-- Oh, attendez une seconde, vous pourriez faire tomber tout un conteneur de vue avec le WebView. Vous pouvez faire une variation de ce qui précède et attendre de libérer le conteneur entier dans shootWebView.

0voto

Can Berk Güder Points 39887

Un simple release message dans dealloc devrait être suffisant.

Votre deuxième problème ressemble à une vue désallouée prématurément, mais je ne peux pas en dire plus sans voir du code.

0voto

TimM Points 193

J'ai eu un problème similaire à celui-ci en utilisant une UIWebView dans OS3 - cette description était un bon point de départ, cependant j'ai trouvé que le simple fait de mettre à zéro le délégué de la vue web avant de libérer la webView a résolu mon problème.

En lisant l'exemple de code (la réponse acceptée - ci-dessus) - cela semble beaucoup trop compliqué. Par exemple, les lignes [webView release] et webView = nil font exactement la même chose étant donné la façon dont l'auteur décrit la variable est déclarée (donc vous n'avez pas besoin des deux). Je ne suis pas non plus entièrement convaincu par toutes les lignes retain et release - mais je suppose que votre kilométrage varie.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X