ios - UIWebView: ¿Cuándo(o cómo) se lanza CFData?
memory-management (3)
después de varios días de golpear mi cabeza contra la pared y pasar noches sin dormir, espero encontrar algo de ayuda aquí. He revisado varias publicaciones aquí, pero ninguna de las respuestas parece proporcionar una resolución para mí.
En resumen, mi problema es que mi aplicación falla después de un uso intensivo (> 10 minutos) de UIWebView (por ejemplo, abrir sitios más grandes de periódicos en serie, uno después del otro).
Para dar más detalles: estoy escribiendo una aplicación para iPhone y desde MainViewController presiono un navegador ViewController en el navegadorController. El navegadorViewController carga un plumín que contiene un UWebView (no creo el WebView programáticamente). UIWebView está conectado usando Interface Builder.
Cuando regreso a Main y luego voy de nuevo a browserViewController, solo recreo el navegadorViewController si es nulo. (Quiero mantener el contenido que está cargado en UIWebView, solo si hay una advertencia de memoria para que esta vista se descargue y liberar toda la memoria utilizada).
En ambos, MainViewController y browserViewController estoy respondiendo a las advertencias de memoria, pero esto parece no proporcionar suficiente alivio.
En cuanto a los instrumentos noté que, por ejemplo, CFData (tienda) sigue aumentando. E incluso si simulo una advertencia de memoria (vea el código a continuación) y llamo a viewDidUnload en browserViewController, CFData permanece asignado y no se libera.
Entonces mi mayor pregunta es:
¿Cómo liberar memoria creada desde "navegación"?
Esto cuenta para dos casos:
- ¿Cómo me aseguro de que viewDidUnload libere correctamente la memoria asignada a mi CFData (tienda)?
- ¿Cómo liberar memoria cuando el usuario sigue cargando páginas en browserViewController?
.
¿Quién maneja CFData?
Vea a continuación mi código de muestra simplificado:
MainViewController.h
// MainViewController.h
#import "myAppDelegate.h"
#import "BrowserViewController.h"
@interface MainViewController : UIViewController {
BrowserViewController *browViewController;
}
- (void) switchToBrowserViewController;
@end
MainViewController.m
// MainViewController.m
#import "MainViewController.h"
@implementation MainViewController
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn''t have a superview.
[super didReceiveMemoryWarning];
[browViewController release];
browViewController = nil;
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[browViewController release];
browViewController = nil;
[super dealloc];
}
- (void) switchToBrowserViewController {
// create new browViewController if needed
if ( browViewController == nil ) {
browViewController = [[BrowserViewController alloc] initWithNibName:@"BrowserViewController" bundle:nil];
}
browViewController.navigationItem.hidesBackButton = YES;
[((myAppDelegate *)[UIApplication sharedApplication].delegate).navController setNavigationBarHidden:YES animated:NO];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration: 1];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:
((myAppAppDelegate *)[UIApplication sharedApplication].delegate).navController.view cache:YES];
[((myAppAppDelegate *)[UIApplication sharedApplication].delegate).navController pushViewController:browViewController animated:NO];
[UIView commitAnimations];
}
@end
BrowserViewController.h
// BrowserViewController.h
#import <UIKit/UIKit.h>
#import "myAppDelegate.h"
@interface BrowserViewController : UIViewController <UIWebViewDelegate> {
IBOutlet UITextField *browserURLField;
IBOutlet UIWebView *browserWebView;
}
@property (nonatomic, retain) UIWebView *browserWebView;
- (void) loadURLinBrowser;
@end
BrowserViewController.m
// BrowserViewController.m
#import "BrowserViewController.h"
@implementation BrowserViewController
@synthesize browserWebView;
- (void)viewDidLoad {
[browserWebView setDelegate:self];
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn''t have a superview.
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
[super viewDidUnload];
[browserWebView stopLoading];
[browserWebView setDelegate:nil];
[browserWebView removeFromSuperview];
[browserWebView release];
browserWebView = nil;
browserURLField = nil;
}
- (void)dealloc {
[browserURLField release];
browserWebView.delegate = nil;
[browserWebView stopLoading];
browserWebView = nil;
[browserWebView release];
[super dealloc];
}
- (void) switchBackToMainViewController {
[((myAppDelegate *)[UIApplication sharedApplication].delegate).navController setNavigationBarHidden:NO animated:NO];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration: 1];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlDown forView:((myAppAppDelegate *)[UIApplication sharedApplication].delegate).navController.view cache:YES];
[((myAppAppDelegate *)[UIApplication sharedApplication].delegate).navController popViewControllerAnimated:NO];
[UIView commitAnimations];
}
- (void) loadURLinBrowser {
NSURL *url = [[NSURL alloc] initWithString:browserURLField.text];
NSMutableURLRequest *request = [[NSURLRequest alloc] initWithURL: url];
[browserWebView loadRequest: request];
[request release];
[url release];
}
@end
He intentado varias recomendaciones de otras publicaciones. Por ejemplo:
1) Cargando una página vacía en WebView.
NSString *html = @"<html><head></head><body></body></html>"; [browserWebView loadHTMLString:html baseURL:nil];
2) usando removeAllCachedResponses en varios lugares en el código anterior
[[NSURLCache sharedURLCache] removeAllCachedResponses];
3) setSharedURLCache tampoco proporcionó alivio (también lo usé en la aplicación AppDelegateDidFinishLaunching).
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil]; [NSURLCache setSharedURLCache:sharedCache]; [sharedCache release];
Desafortunadamente, nada de esto ha ayudado a "borrar el caché" o a liberar memoria asignada por CFData (tienda).
Si alguien pudiera arrojar algo de luz sobre esto y decirme lo que me estoy perdiendo o lo que estoy haciendo mal, lo agradecería mucho.
.
.
Editar:
Después de la respuesta inicial de KiwiBastard, agregué una captura de pantalla que muestra lo que observo en Instruments:
.
.
Editar desde junio de 2010:
Todavía no he podido resolver esto.
En un segundo intento, creé UIWebView completamente programáticamente.
Todavía el mismo problema. Sin embargo, noté un comportamiento extraño. Si cargo, por ejemplo, un documento PDF en la vista web y no despliego la página PDF hacia arriba o hacia abajo, la webView y los datos se publican con éxito. Sin embargo, tan pronto como me desplazo a la segunda página, el trato no funcionará más y mi aplicación se queda sin memoria en algún momento. Esto es totalmente extraño y no puedo resolverlo.
Alguien tiene alguna idea? ¿Ayuda?
Creo que lo que podría estar sucediendo es que su navegador nunca se desasigna, y es probable que viewDidUnload nunca se llame.
Debido a que su MainViewController tiene una variable de tipo BrowserViewController que nunca se lanza, será residente durante la vida de su aplicación. Además, debido a que solo está cambiando las vistas, la vista permanecerá en la memoria también.
¿Puedo sugerirle que intente crear la variable BrowserViewController cuando la necesite y liberarla una vez que haya sido presionada por el controlador de navegación?
BrowserViewController *browViewController = [[BrowserViewController alloc] initWithNibName:@"BrowserViewController" bundle:nil];
browViewController.navigationItem.hidesBackButton = YES;
[((myAppDelegate *)[UIApplication sharedApplication].delegate).navController setNavigationBarHidden:YES animated:NO];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration: 1];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:
((myAppAppDelegate *)[UIApplication sharedApplication].delegate).navController.view cache:YES];
[((myAppAppDelegate *)[UIApplication sharedApplication].delegate).navController pushViewController:browViewController animated:NO];
[UIView commitAnimations];
[browViewController release];
Sé que tendrá un efecto leve en el rendimiento porque tiene que cargar el plumín cada vez, pero definitivamente no quieres almacenar en caché el vc.
Para lanzar CFData solo necesita llamar a CFRelease ( su nombre de objeto CFData ) .
En algún lugar que he leído, este es un error muy conocido con UIWebView. Algunos dicen que deben usar un objeto webview estático para evitar inicializarlo una y otra vez, pero no pudieron encontrar una solución adecuada. Incluso usted sigue el mismo enfoque. Afortunadamente, mi requisito era una vista web simple con una imagen. Así que terminé usando un controlador personalizado con UIImageView y una UITextView sin editar. Funciona bien para mí