iOS:滚动 UITableView 时应用程序崩溃/内存被填充

Posted

技术标签:

【中文标题】iOS:滚动 UITableView 时应用程序崩溃/内存被填充【英文标题】:iOS : app crash / memory is filled in when scrolling a UITableView 【发布时间】:2013-02-04 07:53:52 【问题描述】:

我的应用程序的一个视图显示了图像列表。当我多次滚动该列表时,我的应用程序崩溃了。我用仪器对其进行了分析,似乎列表的单元格在列表滚动时占用了更多内存。

从 tableView:cellForRowAtIndexPath: 返回时,自定义 UITableCell 是否应该“自动释放”? (如果我这样做了,我在 ios 4.3 上会崩溃/在 iOS 5.0 和 6.1 上没问题)

这个自定义 UITableCell 有几张图片被绘制到它的“contentView”中。这些图片实际上是我在其中设置背景图像的自定义 UIButton。

图片由HJManagedImageV管理

自定义 UIButton 的代码:

@implementation ProductTileButtonIpad

@synthesize product;

- (id)initWithFrame:(CGRect)frame andProduct:(Product *)aProduct 

    if (self = [super initWithFrame:frame]) 

        self.product = aProduct;

        self.productTileView = [[[HJManagedImageV alloc] initWithFrame:self.frame] autorelease];
        self.productTileView.callbackOnSetImage = self;
        self.productTileView.url = some picture url

        [[ImageManager instance] manage:self.productTileView];
    

    return self;



#pragma mark -
#pragma mark HJManagedImageV delegate

-(void) managedImageSet:(HJManagedImageV*)mi 

    [self setBackgroundImage:mi.image forState:UIControlStateNormal];


-(void) managedImageCancelled:(HJManagedImageV*)mi 



- (void)dealloc 

    NSLog(@"deallocating ProductTileButtonIpad");

    [self.product release];
    [self.productTileView release];

    [super dealloc];


@end

自定义单元格的代码

@implementation ProductGridCellIpad

@synthesize products, parentController;

- (void)initializeWithProducts:(NSMutableArray *)productsToShow

    self.products = productsToShow;

    // clear possible old subviews
    for (UIView *v in self.contentView.subviews) 
        [v removeFromSuperview];
    

    NSInteger width = 240;
    NSInteger height = 240;

    Product *product0 = [products objectAtIndex:0];

    self.productTile0 = [[[ProductTileButtonIpad alloc] initWithFrame:CGRectMake(12, 12, width, height) andProduct:product0] autorelease];

    [self.productTile0 addTarget:self.parentController action:@selector(selectedProduct:) forControlEvents:UIControlEventTouchUpInside];

    [self.contentView addSubview:self.productTile0];

    [self.productTile0 release];

    if ([self.products count] > 1) 

        Product *product1 = [products objectAtIndex:1];

        self.productTile1 = [[[ProductTileButtonIpad alloc] initWithFrame:CGRectMake(12 + width + 12, 12, width, height) andProduct:product1] autorelease];

        [self.productTile1 addTarget:self.parentController action:@selector(selectedProduct:) forControlEvents:UIControlEventTouchUpInside];

        [self.contentView addSubview:self.productTile1];

        [self.productTile1 release];
    

    if ([self.products count] > 2) 

        Product *product2 = [products objectAtIndex:2];

        self.productTile2 = [[[ProductTileButtonIpad alloc] initWithFrame:CGRectMake(2*(12 + width) + 12, 12, width, height) andProduct:product2] autorelease];

        [self.productTile2 addTarget:self.parentController action:@selector(selectedProduct:) forControlEvents:UIControlEventTouchUpInside];

        [self.contentView addSubview:self.productTile2];

        [self.productTile2 release];
    


- (void)dealloc 

    NSLog(@"deallocating ProductGridCellIpad");

    if(self.products)
        [self.products release];

    if(self.productTile0)
        [self.productTile0 release];

    if(self.productTile1)
        [self.productTile1 release];

    if(self.productTile2)
        [self.productTile2 release];

    [super dealloc];


@end

这是创建单元格的代码:

    NSString *productGridCellIpadIdentifier = @"ProductGridCellIpadIdentifier";

    ProductGridCellIpad *cell = [tableView dequeueReusableCellWithIdentifier:productGridCellIpadIdentifier];

    if(cell == nil) 

        cell = [[ProductGridCellIpad alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:productGridCellIpadIdentifier];

        [cell setFrame:CGRectMake(0, 0, self.view.frame.size.width, 244)];
    

    [cell setParentController:self];
    [cell initializeWithProducts:products];

    return cell;

现在代码在 iOS 4.3 上立即崩溃。它适用于 iOS 5 和 6,但在使用/滚动表格一段时间后,应用程序仍然崩溃。

我不使用 ARC。

我在 dealloc 方法中添加了一些 NSLog 以查看发生了什么,我可以看到很多“deallocating ProductTileButtonIpad”,但我从来没有看到“deallocating ProductGridCellIpad”

我的应用轻松达到 400Mb 的内存使用量。

我在这里做错了什么?

如果你们中的一些人有任何想法,可以帮助我理解的想法,将不胜感激:)

【问题讨论】:

不确定是不是这样,但如果你不使用ARC,你需要自动释放单元格。 问题是你每次都在初始化东西,你应该只做一次初始化。当你使用 tableview 时就像这样 if(cell==nil) //你初始化 //这里只更新值..这里没有初始化.. @iphonic “你每次都在初始化”是什么意思?因为我确实使用细胞回收... 【参考方案1】:

单元格被重复使用,因此您在滚动时不应该看到“dealloc ProductGridCellIpad”。而是验证回收是否真的有效,或者您是否一直在创建新单元:

ProductGridCellIpad *cell = [tableView dequeueReusableCellWithIdentifier:productGridCellIpadIdentifier];

if(cell == nil) 
    cell = [[ProductGridCellIpad alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:productGridCellIpadIdentifier];
    [cell setFrame:CGRectMake(0, 0, self.view.frame.size.width, 244)];
    NSLog("Cell created");

else 
    NSLog("Cell recycled");

如果没问题,我会检查发布。例如,自动释放和释放都有“self.productTile2”,这可能会混淆内存管理。

我还会仔细检查“parentController”,这可能会阻止内容被释放。您需要将其设置为 nil。

【讨论】:

回收似乎运作良好:创建了 4 个单元格(我猜这是因为屏幕上可见 4 个单元格),然后它们都被回收了,但是如果我返回上一个视图,则显示再次列出我可以看到创建了 4 个新单元格。所以如果我继续这样做,内存使用量会增加吗? 关于发布,那么在目前的情况下我应该怎么做呢?释放还是自动释放?我真的不明白它们之间的区别,只是在我调用 release 后我无法触摸 ref... 使用自动释放(当系统在对象不再使用后为您释放时)或使用释放(当您告诉系统对象已准备好在下次释放时释放),但做不要同时使用两者。 所以离开和回来永远不会发布任何东西?那么它可能是 cell.parentController 阻止对象释放。 确实似乎对 parentController 的引用阻止了单元格被释放。怎么可能?我知道如果另一个对象仍然持有对它的引用,则不能释放一个对象,但这是相反的方式吗?那么,这可以与弱引用一起使用吗?【参考方案2】:

是的,当你使用alloc] init.. 时,你显然必须autorelease 你的手机 dequeueReusableCellWithIdentifier 返回自动释放的。

【讨论】:

以上是关于iOS:滚动 UITableView 时应用程序崩溃/内存被填充的主要内容,如果未能解决你的问题,请参考以下文章

iOS:滚动 UITableView 时应用程序崩溃/内存被填充

切换到 iOS 7 后 UITableView 不滚动

按下按钮时滚动以刷新 iOS - 非 UITableView

iOS - UITableView - 快速滚动时神秘地重复自己的行? (模拟器)

滚动 UITableview 时发生崩溃

IOS - 滚动时将 UIView 保持在 UITableView 之上