UIWebView - Когда (или как) выпускается CFData?
После нескольких дней ударов головой о стену и бессонных ночей я надеюсь найти здесь какую-то помощь. Я просмотрел различные посты здесь, но ни один из ответов, кажется, не дает мне решения.
Короче говоря, моя проблема заключается в том, что мое приложение аварийно завершает работу после интенсивного использования (>10 минут) UIWebView (например, при открытии крупных сайтов газетных статей последовательно - один за другим).
Для более подробной информации: я пишу приложение для iPhone, и из MainViewController я помещаю browserViewController в навигационный контроллер. BrowserViewController загружает перо, которое содержит UWebView (я не создаю WebView программно). UIWebView подключается с помощью Interface Builder.
Когда я возвращаюсь в Main, а затем снова возвращаюсь к browserViewController, я воссоздаю browserViewController, только если он равен нулю. (Я хочу сохранить загруженный контент в UIWebView - только при наличии предупреждения о памяти, если это представление будет выгружено и освободит всю использованную память).
В обоих случаях, MainViewController и browserViewController, я отвечаю на предупреждения памяти, но это, кажется, не обеспечивает достаточного облегчения.
Глядя на инструменты, я заметил, что, например, CFData(магазин) продолжает расти. И даже если я имитирую предупреждение о памяти (см. Код ниже) и вызываю viewDidUnload в browserViewController, CFData остается выделенным и не освобождается.
Итак, мой самый большой вопрос:
Как освободить память, созданную от "просмотра"?
Это относится к двум случаям:
я могу убедиться, что viewDidUnload правильно освобождает память, выделенную мои CFData(хранилище)?
освободить память, когда пользователь продолжает загружать страницы в browserViewController?
,
Кто управляет CFData?
Ниже приведен мой упрощенный пример кода:
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
Я испробовал различные рекомендации из других постов. Например:
1) Загрузка пустой страницы в WebView.
NSString *html = @"<html><head></head><body></body></html>"; [browserWebView loadHTMLString:html baseURL:nil];
2) использование removeAllCachedResponses в различных местах в приведенном выше коде
[[NSURLCache sharedURLCache] removeAllCachedResponses];
3) setSharedURLCache также не предоставил облегчения (я также использовал это в приложении AppDelegateDidFinishLaunching).
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil]; [NSURLCache setSharedURLCache:sharedCache]; [sharedCache release];
К сожалению, ничего из этого не помогло "очистить кеш" или освободить память, выделенную CFData(хранилище).
Если бы кто-нибудь мог пролить свет на это и сообщить мне, что я скучаю или делаю неправильно, я был бы очень признателен за это.
,
,
Редактировать:
После первоначального ответа от KiwiBastard я добавил снимок экрана, на котором показано, что я наблюдаю в инструментах:
,
,
Изменить с июня 2010 года:
Я до сих пор не смог решить это.
Во второй попытке я полностью создал программный интерфейс UIWebView.
Все та же проблема. Однако я заметил странное поведение. Если я загружаю, например, документ PDF в webView и не прокручиваю страницу PDF вверх или вниз, webView & data будут успешно освобождены. Однако, как только я прокручиваю до второй страницы, dealloc больше не будет работать, и в какой-то момент моему приложению не хватит памяти. Это совершенно странно, и я не могу решить это.
Кто-нибудь есть идеи? Помогите?
3 ответа
Для выпуска CFData вам нужно только вызвать CFRelease(имявашего объекта CFData).
Где-то я читал, это хорошо известная ошибка в UIWebView. Некоторые говорят, что нужно использовать статический объект webview, чтобы избежать его инициализации снова и снова, но не смогли найти правильного решения. Даже ты придерживаешься того же подхода. К счастью, моим требованием был простой веб-просмотр с изображением. В итоге я использовал собственный контроллер с UIImageView и UITextView без редактирования. У меня отлично работает.
Я думаю, что может случиться так, что ваш браузер никогда не освобождается, а viewDidUnload, вероятно, никогда не вызывается.
Поскольку у вашего MainViewController есть переменная типа BrowserViewController, которая никогда не выпускается, она будет резидентной на протяжении всего жизненного цикла вашего приложения. Кроме того, поскольку вы только переключаете представления, представление также останется в памяти.
Могу ли я предложить вам попробовать создать переменную BrowserViewController, когда вам это нужно, и освободить ее, как только она будет нажата navcontroller, например
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];
Я знаю, что это немного повлияет на производительность, потому что он должен загружать перо каждый раз, но вы явно не хотите кэшировать VC в любом случае?