UIWebView gibt nicht alle Live-Bytes mit ARC frei

Ich baue derzeit eine Navigations-Controller-App in iOS 5.1 auf, die ARC verwendet. Ich muss oft Webseiten anzeigen und habe einen Web-Viewer erstellt, der nur eine UIWebView mit benutzerdefinierten Inhalten an den Seiten ist. Wenn der Benutzer mit dem Betrachten der Seite fertig ist, klickt er auf die Schaltfläche "Zurück", wodurch der gesamte Speicher freigegeben wird, der dem benutzerdefinierten Web-Viewer zugeordnet ist. Mein Problem ist, dass nicht der gesamte Speicher freigegeben zu sein scheint, wenn die Zurück-Taste gedrückt wird. Ich habe eine Spielzeug-App gebaut (auf Github) das sind nur ein paar Tasten mit jeweils einem Ersthelfer, der eine andere Seite aufruft.

<code>@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];
}
</code>

Das CustomWebView ist nur ein UIWebView, das in IB mit der UIWebView-Eigenschaft verknüpft ist

<code>@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];
}
</code>

Mein Problem ist, dass ich die Heap-Baseline auf den anfänglichen ViewController setze, sobald alles geladen ist. Ich überprüfe dann den Heap nach dem Laden einer Seite und kehre dann zum ViewController zurück und erhalte die folgenden Heapshots:

Dies zeigt, dass der Heap nach jedem Klicken auf eine Schaltfläche und jeder Rückkehr zum ViewController weiter wächst, obwohl alle CustomWebView-Komponenten freigegeben werden sollten.

BEARBEITEN:

Das oben beschriebene Beispielprojekt finden Sie hierauf Github

 brendan11. Apr. 2012, 23:26
Ja, Zombies sind deaktiviert
 MishieMoo11. Apr. 2012, 22:34
Hast du sichergestellt, dass Zombies deaktiviert sind?
 wvteijlingen13. Apr. 2012, 14:20
Im Dunkeln gedreht, aber versuchen Sie, Ihre IBOutlet-Eigenschaften zu verbessern (schwach).

Antworten auf die Frage(6)

g auf. Ich habe einen - (ungültigen) Dealloc-Aufruf in Ihre CustomWebView-Klasse eingefügt und dieser wird wie erwartet aufgerufen, wenn die Schaltfläche "Zurück" gedrückt wird.

Sie können versuchen, [[NSURLCache sharedURLCache] removeAllCachedResponses] oder verwenden und dann Ihre eigene Kopie von NSURLCache freigeben, aber offen gesagt UIWebViewnoch nie reinigt vollständig nach sich selbst.

@interface WebViewViewController : UIViewController<UIWebViewDelegate>

<code>- (void)viewDidLoad
{
[super viewDidLoad];
NSString* urlAddress = @"http://www..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;
}
</code>

aber wenn ich mir Ihren benutzerdefinierten Webview-Code ansehe, glaube ich, dass Ihre erstellten Webviews immer noch aktiv sind.

Wenn Sie sicherstellen möchten, dass Ihre Webviews ordnungsgemäß beendet werden, stellen Sie sicher, dass in viewWill / DidDisappear [webview stopLoading] aufgerufen und die Webansicht deaktiviert wird.

(Vorsicht: Wenn Sie eine andere Ansicht verwenden, wird auch die Webansicht beendet. Behandeln Sie diese Probleme also gut.)

In den meisten Fällen hatte ich Probleme mit webViews, bei denen selbst nach dem Öffnen des viewControllers ein Rückruf auf eine webView-Delegate-Methode die App zum Absturz brachte. (Ich denke, ARC verhindert, dass es abstürzt).

Lassen Sie mich wissen, ob das hilft.

 brendan12. Apr. 2012, 02:26
Ich habe ein Beispielprojekt gepostet, um das Problem zu zeigen:github.com/bfruin/WebViewMemory
 Christian Schnorr15. Apr. 2012, 12:46
In Instrumenten getestet und nach 'UIWebView'-Objekten gesucht. Sie verschwinden, wenn Sie den Zurück-Knopf drücken ...
 Nitin Alabur11. Apr. 2012, 01:02
Ich bin mir nicht sicher, wie ich das ARC-Detail in deiner Frage verpasst habe. Jetzt bin ich mir nicht sicher, warum es nicht veröffentlicht wird.
 brendan10. Apr. 2012, 19:53
Calvin, vielen Dank für die Antwort, aber das Aufrufen von stopLoading und das Löschen der Webansicht ändert weder den Heapshot noch die Gesamtzahl der Live-Bytes. Wäre es für diese intensiven Seiten besser, nur in Mobile Safari zu öffnen? Dieses oben vorgestellte Beispielprojekt zeigt, dass es sehr einfach ist zu beweisen, dass die UIWebView auch nach dem Aufheben der Zuweisung einen Speicherbedarf hinterlässt.
 brendan10. Apr. 2012, 22:45
Ich verwende ARC, das das Versenden von Nachrichten zur Freigabe verbietet. Nach meinem Verständnis von ARC wird es automatisch aufgerufen.
 Nitin Alabur10. Apr. 2012, 22:07
Webviews sind im Hinblick auf den Ressourcenverbrauch teuer. Ich bin mir nicht sicher, warum sich deine gesamten Lebendbisse nicht ändern. Geben Sie in Ihren ViewController-IBActions die von Ihnen erstellten Objekte frei, nachdem Sie die ViewController auf Ihrem Navigationsstapel gedrückt haben? Ich habe dieses Detail früher übersehen.
 Nitin Alabur10. Apr. 2012, 22:09
idealerweise sollte es (IBAction) googlePressed sein: (id) sender {CustomWebView * customWebView = [[CustomWebView alloc] initWithStringURL: @ "google.com "]; [self.navigationController pushViewController: customWebView animiert: NEIN]; [customWebView release]; }

estgestellt, dass die Webansicht alles enthält, was geladen wurde, bis gesagt wird, dass etwas anderes geladen werden soll. Die Art und Weise, wie ich das umgehen kann, ist die Verwendung eines JavaScript-Aufrufs in der Methode view will (oder did) dissapear wie folgt:

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

Dies gibt die Erinnerung für mich frei.

 brendan11. Apr. 2012, 23:57
Ich habe diesen Code zu meinem Projekt hinzugefügt und das Speicherproblem konnte nicht behoben werden. Ein Beispielprojekt, das mein Problem zeigt, kann gefunden werdengithub.com/bfruin/WebViewMemory

Sie haben nicht genügend Code gepostet, um sicherzugehen, was gerade passiert. Aber warum sollte der Controller nicht ein SINGLE UIWebView besitzen und nicht bei jedem Tastendruck ein neues erstellen, sondern ihm einfach einen String mit der entsprechenden URL übergeben? Das ist ein besseres Designmuster, das garantiert, dass Sie nie mehr als eines haben.

 brendan12. Apr. 2012, 04:06
Mein Problem ist, dass der auf git veröffentlichte Code anzeigt, dass ich nicht den gesamten Speicher zurückerhalte, wenn ich von einem Ansichtscontroller mit einer Webansicht zurückkehre. Wenn die App nur ein Web-Viewer wäre, könnte dies in Ordnung sein, aber dies ist nur ein kleiner Teil der App, und im Laufe der Zeit erhalte ich möglicherweise Warnungen zu wenig Arbeitsspeicher. Ich würde es vorziehen, diese Speicherprobleme zu vermeiden, aber wäre es in Ordnung, nur den Status einer App in einem viewDidUnload zu speichern und sich keine Sorgen zu machen, solange es keine Lecks gibt?
 brendan11. Apr. 2012, 23:49
Ich habe es mit nur einem in der angegebenen Weise versucht und bekomme die gleichen Probleme. Ich habe ein Beispielprojekt auf Github gepostet, um meine Speicherprobleme zu zeigen.github.com/bfruin/WebViewMemory
 Cliff Ribaudo12. Apr. 2012, 05:07
Wenn Sie an anderer Stelle und zu anderen Zeiten auf Probleme mit wenig Arbeitsspeicher stoßen, liegt möglicherweise ein anderes Problem mit dem Arbeitsspeicher näher daran, wo bzw. wann Sie dieses Problem erhalten. Das hast du vorher nicht erwähnt. Ich würde damit beginnen, dass nur EIN UIWebView in diesem Controller vorhanden ist, und ihm die URL-Zeichenfolgen übergeben und von dort übernehmen. Das Fazit ARC oder nicht in iOS ist wirklich ganz einfach in Bezug auf den Speicher. Wenn Sie dafür verantwortlich sind, es freizugeben, dann IST es Ihr Problem, aber wenn nicht, dann NICHT.
 Cliff Ribaudo12. Apr. 2012, 03:02
Nun, ich habe mir kurz Ihren Code auf Git angesehen und nichts Offensichtliches, aber ich denke, ich muss letztendlich sagen: Was ist das Problem? Sie verwenden ARC. Speicherverwaltung ist NICHT Ihr Problem. Sofern Sie keine undichte Stelle sehen, die NICHT in dem von Ihnen geposteten Screenshot angegeben ist, wo liegt das Problem tatsächlich? Versuchen Sie es mit dem Allocations-Tool und sehen Sie, welche Objekte instanziiert sind und wie viele.

Ich hatte das gleiche Problem, das ich gefunden habeDieses kleine Stück Magie

<code>/*

 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];
</code>
 Daniel Saidi16. Okt. 2014, 11:35
Ich verwende diesen Ansatz in einer App, die mehrere Webansichten auf den Navigationsstapel schiebt, aber es funktioniert nicht. Ich führe die obigen Schritte aus, wenn ich Speicherwarnungen erhalte (Löschen von Webansichten weiter hinten im Stapel und Neuerstellen, wenn die Ansicht wieder angezeigt wird), wenn der übergeordnete Ansichtscontroller entsorgt wird usw., aber dies funktioniert nicht. Zuallererst steigt der Speicherverbrauch, wenn vom Stapel gesprungen wird. Auch bei der Rückkehr nach einer ziemlich tiefen Navigation, bei der ich sicher bin, dass jede Webansicht wie oben freigegeben wird, verbraucht die App immer noch einen großen Teil des insgesamt zugewiesenen Speichers.

Ihre Antwort auf die Frage