viewDidUnload中的内存管理 - 我应该将数组和对象都放在里面吗?

时间:2012-02-08 04:42:16

标签: ios memory-management null viewdidunload

我有一个tableview来加载来自互联网的新闻。我试图在viewDidUnload中删除所有属性。

- (void)viewDidUnload
{
    self.newsArray = nil;
    self.newsTableView = nil;
    self.indicatorView = nil;
//  self.iconDownLoader = nil;
    self.downloadArray = nil;

    [super viewDidUnload];
}

每次应用程序在viewDidUnload中崩溃。如果我评论self.iconDownLoader = nil;,那就没问题。所以任何人都可以告诉我为什么会这样?谢谢你。

--------------------- NewsViewController.m -------------------- ------

//
//  NewsViewController.m
//
//  Created by on 18/01/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import "NewsViewController.h"
#import "ASIHTTPRequest.h"
#import "SBJson.h"
#import "NewsModel.h"
#import "NewsDetailViewController.h"

#define kCustomRowCount 6
#define IconPlaceHolder @"Spinner"

@implementation NewsViewController

@synthesize appDelegate, newsTableViewCell, newsTableView, indicatorView;
@synthesize iconDownLoader, newsArray, downloadArray;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {

        // setup appDelegate
        self.appDelegate = (SydneyAppDelegate *)[[UIApplication sharedApplication] delegate];

        // initial arrays
        self.newsArray = [[NSMutableArray alloc] init];
        self.downloadArray = [[NSMutableArray alloc] init];
    }
    return self;
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];

    if(self.appDelegate.reachable) {
        [self getNews];
    }
    else
    {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"No Connection" message:@"No Internet connection. Please try again later." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alert show];   
    }
}

- (void)viewDidUnload
{
    self.newsArray = nil;
    self.newsTableView = nil;
    self.indicatorView = nil;
//  self.iconDownLoader = nil;
    self.downloadArray = nil;

    [super viewDidUnload];
}

#pragma mark - ASIHTTPRequest

- (void) getNews
{
    NSURL *url = [NSURL URLWithString:@"http://ferrarimaseratisydney.com/api/getPublicNews.html"];
    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
    [request setDelegate:self];
    [request startAsynchronous];
}

- (void) requestFinished:(ASIHTTPRequest *)request
{
    NSString *responseString = [request responseString];
    NSArray *json = [responseString JSONValue];

    for (id aNewsInJson in json)
    {
        NewsModel *aNews = [[NewsModel alloc] initWithJson:aNewsInJson];
        [self.newsArray addObject:aNews];
    }

    [self.indicatorView removeFromSuperview];

    [self.newsTableView reloadData];
}

- (void) requestFailed:(ASIHTTPRequest *)request
{
    NSError *error;
    error = [request error];
}

#pragma mark - Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{   
    [tableView deselectRowAtIndexPath:indexPath animated:YES];

    // Navigation logic may go here. Create and push another view controller.
    NewsDetailViewController *newsDetailViewController = [[NewsDetailViewController alloc] init];

    // transform news array
    newsDetailViewController.news = [self.newsArray objectAtIndex:indexPath.row];

    // Pass the selected object to the new view controller.
    [self.navigationController pushViewController:newsDetailViewController animated:YES];
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [self.newsArray count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"NewsCellIdentifier";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:@"NewsTableViewCell" owner:self options:nil];
        cell = self.newsTableViewCell;
        self.newsTableViewCell = nil;
    }

    // read from newsModel
    NewsModel *news = [self.newsArray objectAtIndex:indexPath.row];

    UILabel *label;
    label = (UILabel *)[cell viewWithTag:10];
    label.text = [NSString stringWithString:news.title];
    label = nil;
    label = (UILabel *)[cell viewWithTag:11];
    label.text = [NSString stringWithString:news.description];
    UIImageView *imageView = (UIImageView *)[cell viewWithTag:12];
    imageView.image = news.image;

    if (news.image == nil)
    {
        imageView.image = [UIImage imageNamed:IconPlaceHolder];

        self.iconDownLoader = [[IconDownLoader alloc] init];
        self.iconDownLoader.url = news.imageUrl;
        self.iconDownLoader.delegate = self;
        self.iconDownLoader.indexPath = indexPath;
        if (self.appDelegate.ip4 == YES)
        {
            self.iconDownLoader.width = 300;
            self.iconDownLoader.height = 150;
        }
        else
        {
            self.iconDownLoader.width = 150;
            self.iconDownLoader.height = 75;
        }
        [self.downloadArray addObject:self.iconDownLoader];
        [self.iconDownLoader start];
    }
    return cell;
}

#pragma mark - IconDownLoaderDelegate

- (void)iconDownLoadFinsh:(NSData *)imageData row:(NSIndexPath *)indexPath {

    UITableViewCell *cell = [self.newsTableView cellForRowAtIndexPath:indexPath];
    UIImageView *imageView = (UIImageView *)[cell viewWithTag:12];
    if (imageData != 0)
    {
        imageView.image = [UIImage imageWithData:imageData];
    }
    else
    {
        imageView.image = [UIImage imageNamed:@"icon57"];
    }
    NewsModel *newsModel = [self.newsArray objectAtIndex:indexPath.row];
    newsModel.image = [UIImage imageWithData:imageData];
}

@end

----------------------- IconDownLoader.m ------------------ -

//
//  IconDownLoader.m
//
//  Created by on 24/11/11.
//  Copyright (c) 2011 __MyCompanyName__. All rights reserved.
//

#import "IconDownLoader.h"
#import "ASIHTTPRequest.h"

@implementation IconDownLoader

@synthesize delegate = _delegate;
@synthesize url = _url;
@synthesize indexPath = _indexPath;
@synthesize width = _width;
@synthesize height = _height;
@synthesize request = _request;

- (void)start {

    NSString *originalString = @"width=%s&height=%s";
    NSString *newString = [NSString stringWithFormat:@"width=%d&height=%d&type=jpg", self.width, self.height];

    NSString *resizedURL = [self.url stringByReplacingOccurrencesOfString:originalString withString:newString];

    NSURL *url = [NSURL URLWithString:[resizedURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
    _request = [ASIHTTPRequest requestWithURL:url];
    if (_indexPath) {
        _request.userInfo = [NSDictionary dictionaryWithObject:_indexPath forKey:@"indexPath"];
    }
    [_request setDelegate:self];
    [_request startAsynchronous];
}

- (void)requestFinished:(ASIHTTPRequest *)request {

    NSInteger statusCode = request.responseStatusCode;
    switch (statusCode) {
        case 401: // Not Authorized: either you need to provide authentication credentials, or the credentials provided aren't valid.
            break;

        case 200: {
            NSData *responseData = [request responseData];
            if (!responseData) {
                UIAlertView *alertView;
                alertView = [[UIAlertView alloc] initWithTitle:@"Oops" message:[NSString stringWithFormat:@"Download failed in row %d", _indexPath.row] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
                return;
            }
            [_delegate iconDownLoadFinsh:responseData row:[request.userInfo objectForKey:@"indexPath"]];
        }
            break;

        default:{

        }
    }
}

- (void)dealloc {
    if (_request != nil) {
        [_request clearDelegatesAndCancel];
    }
}

@end

2 个答案:

答案 0 :(得分:1)

这里没有足够的信息告诉你,但是你可能直接在我们看不到的代码的其他部分发布了iconDownloader而没有在那时设置对nil的引用。

然后在viewDidUnload中,您尝试释放无效的引用。

对于@synthesize,请使用:

@synthesize iconDownLoader = _iconDownloader;

然后修复所有编译器警告以使用self.icondownloder而不是iconDownloader,并消除“release”的所有用法(假设你的属性被标记为保留它应该是)。

事实上,也许你的整个问题是该属性不是一个保留属性,所以你创建了iconDOwnloader并且它会被及时释放。

答案 1 :(得分:1)

通常在viewDidUnload中,您应该只释放和归零所有对您拥有的nib对象的引用。

也就是说,如果消耗大量内存,也可以销毁viewDidUnload中的模型对象。您应该记住viewDidUnloadviewDidLoad的对应物,因此一个好的经验法则是仅销毁您在viewDidUnload中创建的viewDidLoad中的对象。您还应该记住,当视图控制器被释放时,viewDidUnload 被调用 - 只有当它的视图是。

在您的情况下,我不会仅仅因为您在newsArray中创建downloadArrayinit...而发布removeAllObjects和{{1}}。我只会发送{{1}}代替。

对于崩溃,每次单元格需要图像时都会创建一个新的共享图标下载器,这有点尴尬。如果确实需要共享的下载程序实例,则不应为每个单元格重新创建它。看起来你想要保留你在ivar数组中创建的所有下载器,因为每个下载器都是一次性的,并且负责加载单个图像。