2012-04-04 7 views
9

Attualmente sto costruendo un'app di navigazione controller in iOS 5.1 che utilizza ARC. Spesso ho bisogno di visualizzare pagine web e ho creato un visualizzatore web che è solo un UIWebView con alcuni contenuti personalizzati ai lati. Quando l'utente ha terminato di guardare la pagina, ha premuto il pulsante Indietro che dovrebbe rilasciare tutta la memoria associata al visualizzatore Web personalizzato. Il mio problema è che tutta la memoria non sembra essere rilasciata quando viene premuto il pulsante Indietro. Ho creato un'app giocattolo (on github) composta da un paio di pulsanti ciascuno con un primo risponditore che chiama una pagina diversa.UIWebView non libera tutti i byte attivi utilizzando ARC

@implementation ViewController 
-(IBAction)googlePressed:(id)sender 
{ 
    CustomWebView *customWebView = [[CustomWebView alloc] initWithStringURL:@"http://www.google.com"]; 
    [self.navigationController pushViewController:customWebView animated:NO]; 
} 
-(IBAction)ksbwPressed:(id)sender 
{ 
    CustomWebView *customWebView = [[CustomWebView alloc] initWithStringURL:@"http://www.ksbw.com/news/money/Yahoo-laying-off-2-000-workers-in-latest-purge/-/1850/10207484/-/oeyufvz/-/index.html"]; 
    [self.navigationController pushViewController:customWebView animated:NO]; 
} 
-(IBAction)feedProxyPressed:(id)sender 
{ 
    CustomWebView *customWebView = [[CustomWebView alloc] initWithStringURL:@"http://feedproxy.google.com/~r/spaceheadlines/~3/kbL0jv9rbsg/15159-dallas-tornadoes-satellite-image.html"]; 
    [self.navigationController pushViewController:customWebView animated:NO]; 
} 
-(IBAction)cnnPressed:(id)sender 
{ 
    CustomWebView *customWebView = [[CustomWebView alloc] initWithStringURL:@"http://www.cnn.com/2012/04/04/us/california-shooting/index.html?eref=rss_mostpopular"]; 
    [self.navigationController pushViewController:customWebView animated:NO]; 
} 

Il CustomWebView è solo un UIWebView collegato in IB della UIWebView Proprietà

@implementation CustomWebView 

@synthesize webView, link; 

- (id)initWithStringURL:(NSString *) stringURL 
{ 
    if (self = [super init]) { 
     link = stringURL; 
    } 
    return self; 
} 


- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    NSURL *url = [NSURL URLWithString:link]; 
    NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url]; 
    [webView loadRequest:urlRequest]; 
} 

- (void)viewDidUnload 
{ 
    [super viewDidUnload]; 
} 

mio problema è che ho impostato la linea di base mucchio di essere il primo ViewController una volta che tutto è stato caricato. Ho quindi controllare il mucchio dopo il caricamento di una pagina per poi tornare al ViewController e ottenere i seguenti heapshots:

Heapshots

Il che dimostra che dopo ogni serie di clic su un pulsante e tornando al ViewController mucchio continua a crescere anche se CustomWebView dovrebbe essere rilasciato.

EDIT:

Il progetto di esempio sopra descritto può essere trovato on github

+0

Hai fatto in modo zombie sono disabilitate? – MishieMoo

+0

Sì, gli zombi sono disabilitati – brendan

+0

Girati al buio ma prova a rendere le tue proprietà IBOutlet (deboli). – Rengers

risposta

0

I havent usato ARC ancora, ma guardando il tuo codice di WebView personalizzato, penso tue visualizzazioni che si è creato sono ancora vivi.

se si vuole essere sicuri che le proprie visioni web vengano uccise correttamente, assicurarsi che nella viewWill/DidDisappear, chiamare [webview stopLoading] e nil webview.

(attenzione: se si sta spingendo un'altra vista, poi anche il WebView sarà killd, quindi assicuratevi di gestire tali problemi e)

questioni più delle volte, che ho avuto con visualizzazioni in cui anche dopo schioccare la viewController , una chiamata a un metodo delegato di webView causerebbe il crash dell'applicazione. (Suppongo che ARC gli impedisca di andare in crash).

Fatemi sapere se questo aiuta.

+0

Calvin, grazie per la risposta, ma chiamando stopLoading e nil'ing la webview non cambia l'heapshot o il totale dei byte live. Per queste pagine intensive, sarebbe meglio semplicemente aprire in Mobile Safari? Questo esempio di progetto mostrato sopra mostra che è molto facile dimostrare che UIWebView lascia un footprint di memoria anche dopo che tutto è stato deallocato. – brendan

+0

Le visualizzazioni Web sono costose in termini di consumo di risorse. Non sono sicuro del motivo per cui i tuoi morsi in diretta non cambiano. Nel tuo ViewController IBActions, dopo aver spinto i viewcontrollers sul tuo stack nav, stai rilasciando gli oggetti che stai creando? Ho perso quel dettaglio prima. –

+0

idealmente dovrebbe essere- (IBAction) googlePressed: (id) mittente { CustomWebView * customWebView = [[CustomWebView alloc] initWithStringURL: @ "http://www.google.com"]; [self.navigationController pushViewController: customWebView animato: NO]; [versione customWebView]; } –

0

Brendan un paio di punti:

Non hai inserito il codice abbastanza per essere sicuri di ciò che sta accadendo. Ma perché il controller non possiede un UIWebView SINGLE e invece di crearne un altro su ciascun pulsante, basta semplicemente passarlo a una stringa con l'URL pertinente? Questo è un modello di design migliore che ti garantirà di non averne più di uno in giro.

+1

Ho provato con uno solo nel modo specificato e ottengo gli stessi problemi. Ho pubblicato un progetto di esempio su github per mostrare i miei problemi di memoria. https://github.com/bfruin/WebViewMemory – brendan

+0

Bene, ho dato una rapida occhiata al codice su Git e nulla di ovvio, ma suppongo di dover dire in ultima analisi: qual è il problema? Stai usando ARC. La gestione della memoria NON è il tuo problema. A meno che non stiate vedendo una perdita, che NON è indicata nella schermata, avete postato quale è effettivamente il problema? Prova a usare lo strumento Allocazioni e vedere quali oggetti sono istanziati e i loro conteggi. –

+0

Il mio problema è che il codice pubblicato su git mostra che non riesco a recuperare tutta la memoria al ritorno da un controller di visualizzazione che ha una vista web. Se l'app fosse solo un visualizzatore Web, potrebbe essere ok, ma questa è una parte minore dell'app e alla fine ricevo avvisi di memoria insufficiente nel tempo. Preferirei evitare questi problemi di memoria, ma sarebbe giusto salvare lo stato di un'app in un viewDidUnload e non preoccuparti se non ci sono perdite? – brendan

1

Ho usato ARC per un po 'di tempo e in passato ho scoperto che la vista web si aggrappa a qualsiasi cosa sia stata caricata fino a quando non viene detto di caricare qualcos'altro. Il mio modo di andare in giro questo è quello di utilizzare una chiamata javascript nella vista sarà (o fatto) metodo di scomparire in questo modo:

- (void)viewWillDisappear:(BOOL)animated 
{ 
    [super viewWillDisappear:animated]; 
    [self.webView loadHTMLString:@"<html></html>" baseURL:nil]; 
} 

Questo rilascia la memoria per me.

+3

Ho aggiunto questo codice al mio progetto e non ha risolto il problema di memoria. Un esempio di progetto che mostra il mio problema può essere trovato https://github.com/bfruin/WebViewMemory – brendan

1

UIWebView è un maiale memorabile e non rilascia una buona memoria. Ho messo una chiamata - (void) dealloc nella tua classe CustomWebView e si chiama come previsto quando si preme il pulsante Indietro.

Si può provare a [[NSURLCache sharedURLCache] removeAllCachedResponses] o utilizzare e quindi liberare la vostra copia di NSURLCache, ma francamente UIWebView mai pulisce completamente dopo se stessa.

7

ho avuto il problema stesso-ish, l'ho trovato this little piece of magic

/* 

There are several theories and rumors about UIWebView memory leaks, and how 
to properly handle cleaning a UIWebView instance up before deallocation. This 
method implements several of those recommendations. 

#1: Various developers believe UIWebView may not properly throw away child 
objects & views without forcing the UIWebView to load empty content before 
dealloc. 

Source: http://stackoverflow.com/questions/648396/does-uiwebview-leak-memory 

*/   
[webView loadHTMLString:@"" baseURL:nil]; 

/* 

#2: Others claim that UIWebView's will leak if they are loading content 
during dealloc. 

Source: http://stackoverflow.com/questions/6124020/uiwebview-leaking 

*/ 
[webView stopLoading]; 

/* 

#3: Apple recommends setting the delegate to nil before deallocation: 
"Important: Before releasing an instance of UIWebView for which you have set 
a delegate, you must first set the UIWebView delegate property to nil before 
disposing of the UIWebView instance. This can be done, for example, in the 
dealloc method where you dispose of the UIWebView." 

Source: UIWebViewDelegate class reference  

*/ 
[webView setDelegate:nil]; 


/* 

#4: If you're creating multiple child views for any given view, and you're 
trying to deallocate an old child, that child is pointed to by the parent 
view, and won't actually deallocate until that parent view dissapears. This 
call below ensures that you are not creating many child views that will hang 
around until the parent view is deallocated. 
*/ 

[webView removeFromSuperview]; 
+0

http://www.codercowboy.com/code-uiwebview-memory-leak-prevention/ – LordParsley

+1

Io uso questo approccio in un'app che spinge diverse viste Web sullo stack di navigazione, ma non funziona.Faccio quanto sopra quando ricevo avvertimenti sulla memoria (cancellando le viste web più indietro nello stack, quindi ricreandole quando riappare la vista), quando il controller della vista genitore è eliminato, ecc, ma non funziona. Prima di tutto, il consumo di memoria sale quando si apre la pila. Inoltre, quando ritorno dopo una navigazione piuttosto profonda, in cui sono sicuro che ogni vista web è deallocata come sopra, l'app consuma ancora molta della memoria totalmente allocata. –

0

@interface WebViewViewController : UIViewController<UIWebViewDelegate>

- (void)viewDidLoad 
{ 
[super viewDidLoad]; 
NSString* urlAddress = @"http://www.google.com"; 
NSURL *url = [NSURL URLWithString:urlAddress]; 
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url]; 
self.webView.delegate = self; 
[self.webView loadRequest:requestObj]; 
} 

-(void)viewWillDisappear:(BOOL)animated{ 
[super viewWillDisappear:animated]; 
[_webView stopLoading]; 
[_webView loadHTMLString:@"<html><head></head><body></body></html>" baseURL:nil]; 
[_webView stopLoading]; 
[_webView removeFromSuperview]; 
} 

- (void)dealloc 
{ 
[_webView stopLoading]; 
} 

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error 
{ 
[_webView stopLoading]; 
_webView = nil; 
} 
Problemi correlati