2012-08-05 5 views
10

Je suis en train de fixer un tas de fuites que mon UIWebView est à l'origine et ne peuvent pas trouver leur origine, ni une solution de contournement. Ce que je fais est d'obtenir le contenu du Web par le biais d'une demande de réseau, puis assembler mon code HTML et le charger à la volée:fuites de mémoire avec UIWebView et Javascript

NSString* body = <some HTML>; 
NSString* html = [NSString stringWithFormat:kHTMLTemplate, [self scripts], [self styles], body]; 
[_webView loadHTMLString:html 
       baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]]; 

Chaque fois qu'il ya du nouveau contenu disponible, j'execute loadHTMLString à nouveau pour rafraîchir l'affichage Web . Je réutilise la même vue web, le même contrôleur, même chose.

Instruments montre un modèle très étrange où tous les objets sont d'ordre général fuite des blocs de différentes tailles et aucune d'entre eux a des informations qui s'y rattachent: pas de bibliothèque responsable, pas de cadre responsable, etc. Chaque fois loadHTMLString est exécuté , de nouvelles fuites sont ajoutées.

Il semble qu'il y ait plusieurs threads dans S.O. environ UIWebView fuite de mémoire. J'ai essayé toutes les suggestions que j'ai trouvées (par exemple, mettre le NSURLCache à zéro, ou le réinitialiser, j'ai essayé de libérer l'UIWebView existant et d'en allouer un nouveau chaque fois que j'ai de nouvelles données, etc.) mais rien n'a aidé.

Mes recherches jusqu'à maintenant conduire à un résultat clair: il semble que les fuites ne sont présents que si le code HTML je charge dans la vue contient du Javascript. Si vous remarquez la chaîne html ci-dessus, elle est composée de plusieurs composants; on est [self scripts] qui est une fonction qui retourne simplement:

return @"<script type='text/javascript' src='jquery-1.4.4.min.js'></script>" 
     "<script type='text/javascript' src='jmy.js'></script>"; 

Si je retire cela, pas de fuite sont là. Mais les fuites apparaissent dès que j'ajoute une balise <script> à mon HTML. Ils apparaissent même si j'inclure simplement le fichier jquery (ou tout autre fichier js, à ce sujet):

return @"<script type='text/javascript' src='jquery-1.4.4.min.js'></script>"; 

Ainsi, la question: quelqu'un at-il une idée de ce qui se passe ici? Clairement inclure un fichier Javascript dans mon HTML rend la mémoire de fuite UIWebView.

Le fait que les fuites apparaissent à la fois quand je réutiliser le même objet UIWebView ou quand j'instancier un nouveau chaque fois que j'ai contenu, me conduit à penser qu'il doit y avoir quelque chose dans la manière dont les fichiers javascript sont gérés par loadHTMLString qui conduit aux fuites.

Est-ce que quelqu'un sait comment cela pourrait être fixé?

enter image description here

+0

Cela peut être un bogue dans UIWebView. http://blog.techno-barje.fr/post/2010/10/04/UIWebView-secrets-part1-memory-leaks-on-xmlhttprequest/ –

+0

@ H2CO3: merci, j'ai aussi essayé ça ... pas d'améliorations. .. – sergio

+0

pense que nous sommes sauvés par iOS 8. Commander cette réponse sur WKWebView http://stackoverflow.com/questions/16514230/massive-memory-leak-in-ui-uiwebview –

Répondre

11

J'ai finalement trouvé quelques indices à ce qui se passe et surtout une solution de contournement que je voudrais partager.

Je peux confirmer que l'inclusion simple certains fichier javascript a été à l'origine d'une fuite de mémoire sur reload de la vue Web. J'ai même essayé de construire un fichier avec le contenu HTML, puis le charger dans le UIWebView à loadRequest, et de le recharger par reload; les fuites étaient toujours là. Je posterai un radar pour ça.

Ce qui m'a sauvé utilisais innerHTML pour mettre à jour le contenu de l'affichage Web.Au lieu de compter sur reload ou loadHTMLString, j'initialisés mon avis web avec un corps vide (je veux dire, la section head était là, y compris tous les JS requis/fichiers CSS), puis mis à jour ce réglage document.body.innerHTML:

body = [body stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; 
[webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"setBody(\"%@\");", body]]; 

avec sera crée en défini comme ceci:

var setBody = function(body) { 
    document.body.innerHTML = body; 
} 

J'ai obtenu deux avantages: la mise à jour de l'affichage Web est devenu très vite (ce qui est un effet de ne pas mettre à jour le DOM, qui d'autre part n'est pas tout à fait souhaitable dans l'ensemble), et aucune fuite de mémoire n'exécutait l'application sous Instruments. L'inconvénient était que je devais affiner un couple de conditions où l'application fonctionnait bien; plus précisément:

  1. chargement de la vue Web (même avec une page corps vide) prendre beaucoup, vous devez synchroniser la première mise à jour de son contenu lorsque le DOM est prêt;

  2. webViewDidFinishLoading ne semble pas fiable: il est exécuté avant que document.readyState ne devienne complete;

  3. document.documentElement.height, la voie officielle de récupérer la hauteur de page ne semble pas fiable aussi: solution de contournement est d'obtenir le « style calculé » de la partie body et lire sa valeur height.

Espérons que cela aide quelqu'un d'autre qui trouve que ses vues Web fuient la mémoire.