WebView 导致巨大的内存泄漏

发布于 2024-11-05 15:36:52 字数 5702 浏览 0 评论 0原文

我的应用程序中有一个表视图控制器。每个单元格都是一个网站的名称。当点击一个单元格时,将创建一个 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

乖不如嘢 2024-11-12 15:36:52

您仍然在 initWithPath 末尾保留了对 self.view 的额外引用,应该将其释放。

更多解释: [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] 为您提供一个保留计数为 1 的 UIView (因为您刚刚分配了它)。将其分配给 self.view 会将其保留计数增加 1,因此目前为 2。当你的控制器被释放时,它会减少 self.view 中对象的保留计数,但计数仍然是 1,所以它永远不会被释放。你应该这样做:

UIView* myView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
self.view = myView;
[myView release];

You still hold an extra reference to self.view at the end of initWithPath which should be released.

A bit more explanation: [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] gives you a UIView with a retain count of 1 (since you have just allocated it). Assigning it to self.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 in self.view, but the count is still 1, so it never gets deallocated. You should do this:

UIView* myView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
self.view = myView;
[myView release];
无言温柔 2024-11-12 15:36:52

避免 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文