WebView 导致巨大的内存泄漏
我的应用程序中有一个表视图控制器。每个单元格都是一个网站的名称。当点击一个单元格时,将创建一个 Web 视图控制器,传递一个 url 并将其推送到视图。 相当标准吧?
如果我进出网页视图的次数足够多,我就会开始收到内存警告,直到应用程序崩溃。
我在我的实现中看不到任何地方没有正确发布任何内容。是否有一些我可能遗漏的特定于 webview 的最佳实践或重要的委托方法?
这让我发疯,因为涉及的代码很少,但我似乎仍然找不到问题。 有人可以帮助我找出导致此内存问题的原因,或者预先向我推荐一篇文章或教程,以便我了解如何正确执行此操作吗?
表格视图控制器
@implementation LinkListViewController
@synthesize linksArray;
@synthesize wvcontroller;
#pragma mark -
#pragma mark Initialization
- (void)loadView {
[super loadView];
table = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
[self pushViewController:table animated:YES];
table.tableView.delegate = self;
table.tableView.dataSource = self;
table.title = @"Links"; // Tab Bar Title
[self.view addSubview:table.view];
}
-(id) initWithTabBar {
if ([self init]) {
self.tabBarItem.image = [UIImage imageNamed:@"events.png"];
}
return self;
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [linksArray count];;
}
#define LABEL_TAG 7777
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
LinkData *ld;
ld=[linksArray objectAtIndex:indexPath.row];
static NSString *CellIdentifier = @"Cell";
UILabel *label = nil;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
CGRect frame;
frame.origin.x = 0;
frame.origin.y = 10;
frame.size.width = 100;
frame.size.height = 30;
frame.origin.x = 0;
frame.size.width =290;
label = [[[UILabel alloc] initWithFrame:frame] autorelease];
label.tag = LABEL_TAG;
[cell.contentView addSubview:label];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
} else {
label = (UILabel *) [cell.contentView viewWithTag:LABEL_TAG];
}
label.text = [ld.linkname copy];
label.textColor=[UIColor blackColor];
return cell;
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
LinkData *ld=[linksArray objectAtIndex:indexPath.row];
if ([ld.linktype isEqualToString:@"webpage"]) {
if (wvcontroller) {
[wvcontroller release];
wvcontroller=nil;
}
wvcontroller= [[WebViewController alloc]initWithPath:ld.linkurl];
wvcontroller.title=ld.linkname;
[table.navigationController pushViewController:wvcontroller animated:YES];
}
}
Web 视图控制器
#import "WebViewController.h"
@implementation WebViewController
@synthesize webView;
@synthesize path;
@synthesize showsync;
@synthesize activity;
-(id)initWithPath:(NSString *)thepath
{
if ( [self init]) {
self.path= [thepath copy];
self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
[self.view setBackgroundColor:[UIColor blackColor]];
[self loadView];
}
return self;
}
- (void)loadView {
//Create a URL object.
webView = [[UIWebView alloc] initWithFrame:self.view.frame];
webView.autoresizesSubviews = YES;
webView.autoresizingMask=(UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth);
//set the web view delegates for the web view to be itself
self.webView.scalesPageToFit = YES;
self.webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
self.webView.delegate = self;
//Create a URL object.
NSURL *url = [NSURL URLWithString:path];
//URL Requst Object
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
//add the web view to the content view
[self.view addSubview:webView];
//load the URL into the web view.
[webView loadRequest:requestObj];
//[webView release], webView = nil;
activity = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activity.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
activity.center = self.view.center;
[self.view addSubview: activity];
}
/*
If you need to do additional setup after loading the view, override viewDidLoad. */
- (void)viewDidLoad {
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.webView;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)dealloc {
[webView release];
[activity release];
webView=nil;
[path release];
[super dealloc];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc. that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
[activity startAnimating];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[activity stopAnimating];
}
I have a table view controller in my app. Each cell is the name of a website. When a cell is hit a web view controller is created, passed a url and pushed to the view.
Pretty standard right?
If I go in and out of a web view enough times I begin to get memory warnings until the app crashes.
I can't see anywhere in my implementation where I'm not releasing anything properly. Is there some webview specific best practices or important delegate methods I might have left out?
This is driving me crazy because there is very little code involved and yet I still can't seem to find the problem.
Could somebody help me discover what is causing this memory problem or prehaps refer me to an article or tutorial so I can see how to do this properly?
Table View Controller
@implementation LinkListViewController
@synthesize linksArray;
@synthesize wvcontroller;
#pragma mark -
#pragma mark Initialization
- (void)loadView {
[super loadView];
table = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
[self pushViewController:table animated:YES];
table.tableView.delegate = self;
table.tableView.dataSource = self;
table.title = @"Links"; // Tab Bar Title
[self.view addSubview:table.view];
}
-(id) initWithTabBar {
if ([self init]) {
self.tabBarItem.image = [UIImage imageNamed:@"events.png"];
}
return self;
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [linksArray count];;
}
#define LABEL_TAG 7777
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
LinkData *ld;
ld=[linksArray objectAtIndex:indexPath.row];
static NSString *CellIdentifier = @"Cell";
UILabel *label = nil;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
CGRect frame;
frame.origin.x = 0;
frame.origin.y = 10;
frame.size.width = 100;
frame.size.height = 30;
frame.origin.x = 0;
frame.size.width =290;
label = [[[UILabel alloc] initWithFrame:frame] autorelease];
label.tag = LABEL_TAG;
[cell.contentView addSubview:label];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
} else {
label = (UILabel *) [cell.contentView viewWithTag:LABEL_TAG];
}
label.text = [ld.linkname copy];
label.textColor=[UIColor blackColor];
return cell;
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
LinkData *ld=[linksArray objectAtIndex:indexPath.row];
if ([ld.linktype isEqualToString:@"webpage"]) {
if (wvcontroller) {
[wvcontroller release];
wvcontroller=nil;
}
wvcontroller= [[WebViewController alloc]initWithPath:ld.linkurl];
wvcontroller.title=ld.linkname;
[table.navigationController pushViewController:wvcontroller animated:YES];
}
}
Web View Controller
#import "WebViewController.h"
@implementation WebViewController
@synthesize webView;
@synthesize path;
@synthesize showsync;
@synthesize activity;
-(id)initWithPath:(NSString *)thepath
{
if ( [self init]) {
self.path= [thepath copy];
self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
[self.view setBackgroundColor:[UIColor blackColor]];
[self loadView];
}
return self;
}
- (void)loadView {
//Create a URL object.
webView = [[UIWebView alloc] initWithFrame:self.view.frame];
webView.autoresizesSubviews = YES;
webView.autoresizingMask=(UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth);
//set the web view delegates for the web view to be itself
self.webView.scalesPageToFit = YES;
self.webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
self.webView.delegate = self;
//Create a URL object.
NSURL *url = [NSURL URLWithString:path];
//URL Requst Object
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
//add the web view to the content view
[self.view addSubview:webView];
//load the URL into the web view.
[webView loadRequest:requestObj];
//[webView release], webView = nil;
activity = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activity.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
activity.center = self.view.center;
[self.view addSubview: activity];
}
/*
If you need to do additional setup after loading the view, override viewDidLoad. */
- (void)viewDidLoad {
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.webView;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)dealloc {
[webView release];
[activity release];
webView=nil;
[path release];
[super dealloc];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc. that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
[activity startAnimating];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[activity stopAnimating];
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您仍然在
initWithPath
末尾保留了对self.view
的额外引用,应该将其释放。更多解释:
[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]]
为您提供一个保留计数为 1 的UIView
(因为您刚刚分配了它)。将其分配给 self.view 会将其保留计数增加 1,因此目前为 2。当你的控制器被释放时,它会减少 self.view 中对象的保留计数,但计数仍然是 1,所以它永远不会被释放。你应该这样做:You still hold an extra reference to
self.view
at the end ofinitWithPath
which should be released.A bit more explanation:
[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]]
gives you aUIView
with a retain count of 1 (since you have just allocated it). Assigning it toself.view
bumps its retain count by 1, so it is 2 at the moment. When your controller is deallocated, it takes care of decreasing the retain count of the object inself.view
, but the count is still 1, so it never gets deallocated. You should do this:避免 UIWebView 内存问题的一种方法是一次仅创建一个 WebView,并在应用程序的整个生命周期中重复使用它。也就是说,不释放它,而是重用它。
One way to avoid UIWebView memory problems is to create only one webview one time, and reuse it for the whole life of the application. That is, not release it but reuse it.