iphone - UITableView 无缘无故变得缓慢且无响应

标签 iphone objective-c ios xcode

似乎在使用了一段时间后,我的应用程序在 UITableViewController 上变得缓慢且无响应。有一些非常密集的方法,但一旦完成这些方法,它们就没有理由影响应用程序的整体性能。

我一直在使用仪器来确定它可能是什么,但事实证明它非常不确定。首先,我无法通过一种方法重现问题,它似乎只是在一般情况下发生的。根据线程使用情况,使用 CPU 的主要是我的 cellForRowAtIndexPath。虽然我确实做了很多计算,但它并没有解释两件事。 1)为什么这个问题会随着时间的推移而发展。当我第一次启动应用程序时,我可以上下滚动 TableViewController 几次,一切都非常流畅。 2) 即使我移除了除 3 或 4 个单元格之外的所有单元格,仍然没有响应。

另一个观察结果是,modal ViewController 在应用程序首次加载时有一个非常流畅的动画,但最终会出现这种可怕的锯齿状动画,有时只有 1 或 2 帧,稍后。同样,在关闭此 Modal(包括 managedObjectContext 保存)时有一些相当复杂的计算,并且它确实接近揭示下面的 UITableView(意味着一些 cellForRowAtIndexPath ) 但仅靠这两件事无法使动画达到几乎 0 fps。所有这一切让我相信资源正在被用完并且不会返回。现在遗憾的是,我对 iOS 环境的了解还不够多,无法确定接下来的假设,但这里是:

  • 内存。简而言之,它不会真的是这样吧?尽管我的应用程序非常占用内存并包含大量图像,但我很确定 iOS 会在内存开始用完时从 RAM 中删除内容。最重要的是,诊断显示我在应用程序中遇到大量延迟时有 50MB 可用空间。

  • 中央处理器。我认为可能是某些东西耗尽了我所有的 CPU,就像上次我遇到性能问题时一样(我调用了一个方法的无限循环,糟糕)但是,该应用程序似乎只在需要时才使用 CPU。换句话说,当应用程序空闲时,它会回到 0 使用率。只是为了解释这是多么重要;如果作为一种资源的 CPU 正在被某种东西吃掉,那么它就可以解释为什么 cellForRowAtIndexPath 随着时间的推移而挣扎。但是,由于它没有被吃掉,所以没有理由长时间使用该应用程序会导致 cellForRowAtIndexPath 使用过多的 CPU。因此,我看不出我的问题是由 CPU 被占用引起的。

当然,由于这是我能想到的仅有的两种资源,所以我完全被难住了。我不知道为什么应用程序会随着时间的推移变慢。实际上,我看不出它怎么可能是代码,因为应用程序在第一次启动时运行良好,而且我看不出它怎么可能是资源,因为似乎还有很多资源。

由于我是 Objective-C/iOS 开发的新手,我怀疑我遗漏了什么。如果您能帮助我识别它,我将不胜感激。

更新: 发布我的 cellForRowAtIndexPathwillDisplayCell 代码:

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
myEntityForConfigureCell = [self.fetchedResultsController objectAtIndexPath:indexPath];

searchResultForConfigureCell = [searchResults objectForKey:myEntityForConfigureCell.trackId];


// Example of how I fill in cell information. There are far more than shown.
nameLabelForConfigureCell = (UILabel *)[cell viewWithTag:1001];
nameLabelForConfigureCell.text = myEntityForConfigureCell.name;

genreLabelForConfigureCell = (UILabel *)[cell viewWithTag:1002];
genreLabelForConfigureCell.text = searchResultForConfigureCell.genre;

// Example of how I do the ImageViews using AFNetworking
imageForConfigureCell = (UIImageView *)[cell viewWithTag:1000];
[imageForConfigureCell setImageWithURL:[NSURL URLWithString: searchResultForConfigureCell.artworkURL60]];
imageForConfigureCell.layer.cornerRadius = 9.4;
imageForConfigureCell.layer.masksToBounds = YES;

// The RateView for showing a start rating
rateViewForConfigureCell = [[DYRateView alloc] initWithFrame:CGRectMake(73, 44, 75, 12) fullStar:[UIImage imageNamed:@"StarFullSmall.png"] emptyStar:[UIImage imageNamed:@"StarEmptySmall.png"]];
rateViewForConfigureCell.rate = [searchResultForConfigureCell.rating floatValue];
rateViewForConfigureCell.alignment = RateViewAlignmentLeft;
[cell.contentView addSubview:rateViewForConfigureCell];

// Setting Sections for all Core Data entries only if BOOL isLoading is YES, but **has** finished downloading informaton, i.e. just before completion.

if ([searchResults count] == [fetchedResultsController.fetchedObjects count]) {

    if (isLoading) {
        NSLog(@"Is loading so Setting Sections");
        isLoading = NO;
        [self cycleThroughEntriesAndSetSection];
    }
    else if (!isLoading){
        [loadingHudView removeFromSuperview];
    }

}

}

现在对于 willDisplayCell:

-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
myEntityForDisplayCell = [self.fetchedResultsController objectAtIndexPath:indexPath];
searchResultForDisplayCell = [searchResults objectForKey:myEntityForDisplayCell.trackId];

UILabel *currentPrice = (UILabel *)[cell viewWithTag:1003];

if ([myEntityForDisplayCell.price1 floatValue] == [searchResultForDisplayCell.price2 floatValue]) {
   currentPrice.textColor = [UIColor blackColor];
}
else if ([myEntityForDisplayCell.price1 floatValue] > [searchResultForDisplayCell.price2 floatValue])
{
    currentPrice.textColor = [UIColor colorWithRed:0 green:0.9 blue:0 alpha:1];
}
else if ([myEntityForDisplayCell.price1 floatValue] < [searchResultForDisplayCell.price2 floatValue])
{
    currentPrice.textColor = [UIColor redColor];
}

cell.backgroundColor = nil;

if (isLoading) {
    loadingHudView.numOne = [searchResults count];
    loadingHudView.numTwo = [fetchedResultsController.fetchedObjects count];
    [loadingHudView setNeedsDisplay];
}

if ([searchResults count] == [fetchedResultsController.fetchedObjects count]) {

    [loadingHudView removeFromSuperview];

    if ([myEntityForDisplayCell.price1 floatValue] < [searchResultForDisplayCell.price2 floatValue]) {

        cell.backgroundColor = nil;
    }
    else
    {
        cell.backgroundColor = [UIColor colorWithRed:1 green:0.85 blue:0 alpha:0.45];
    }
}

}

更新 2:使用分析器工具找到了一些东西。我不完全确定它是如何泄漏的,但无论如何都会发生。

SearchResult *searchResult = [[SearchResult alloc] init];

for (id i in fetchedResultsController.fetchedObjects) {
    MyEntity *myEntity = i;
    searchResult = [searchResults objectForKey:myEntity.id];

每次将新实体添加到 Core Data 数据库时,这段代码可能会执行一次,即不是特别频繁。

最佳答案

三个观察:

  1. 你说“很多图像我很确定 iOS 会在内存开始用完时从 RAM 中删除一些东西”——仅供引用,如果你使用的是 imageNamed,它不是非常擅长管理你的内存。当我做图像缓存时,我使用我自己的NSCache。如果您使用大图像,这一点尤其重要。

  2. 参见 WWDC 2012 - Building Concurrent User Interfaces on iOS有关如何使用 Instruments 确定性能瓶颈所在的实际示例。

  3. 最后,参见 Finding leaks with Instruments有关如何使用 Instruments 查找泄漏的指导。另外,不要忽视 static analyzer ,如果您不使用 ARC,或者如果您使用任何 Core Foundation 调用,这很重要。

除此之外,如果不查看代码就很难确定问题的根源。


更新:

现在您已经提供了一些源代码,让我眼前一亮的是 rateViewForConfigureCelladdSubview。鉴于单元格被重复使用,您将重复将其添加到单元格中。所以,我建议更换:

// The RateView for showing a start rating
rateViewForConfigureCell = [[DYRateView alloc] initWithFrame:CGRectMake(73, 44, 75, 12) fullStar:[UIImage imageNamed:@"StarFullSmall.png"] emptyStar:[UIImage imageNamed:@"StarEmptySmall.png"]];
rateViewForConfigureCell.rate = [searchResultForConfigureCell.rating floatValue];
rateViewForConfigureCell.alignment = RateViewAlignmentLeft;
[cell.contentView addSubview:rateViewForConfigureCell];

与:

// The RateView for showing a start rating
rateViewForConfigureCell = (DYRateView *)[cell.contentView viewWithTag:1005]
if (!rateViewForConfigureCell) {
    rateViewForConfigureCell = [[DYRateView alloc] initWithFrame:CGRectMake(73, 44, 75, 12) fullStar:[UIImage imageNamed:@"StarFullSmall.png"] emptyStar:[UIImage imageNamed:@"StarEmptySmall.png"]];
    rateViewForConfigureCell.alignment = RateViewAlignmentLeft;
    rateViewForConfigureCell.tag = 1005;
    [cell.contentView addSubview:rateViewForConfigureCell];
}
rateViewForConfigureCell.rate = [searchResultForConfigureCell.rating floatValue];

要么这样做,要么将 DYRateView 添加到您的单元格原型(prototype)。

关于iphone - UITableView 无缘无故变得缓慢且无响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14201483/

相关文章:

ios - 自动拍照

ios - 在关闭 View 之前调用委托(delegate)方法

ios - 检查应用程序是否已安装

ios - 从 viewDidLoad 只加载一次 UIimage

iphone - NSURL连接失败问题

ios - 使用 Objective-C 的 HTTP 客户端上传图像

ios - 使用[popviewcontroller]传回数据,并重新加载数据

ios - 从另一个应用程序打开设置应用程序

ios - ABAddressBook API 在大量使用该应用程序后停止工作?

ios - 从图像 iOS 制作视频