3

似乎在使用了一点之后,我的应用程序在UITableViewController. 有一些非常密集的方法,但一旦完成,就没有理由影响应用程序的整体性能。

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

另一个观察结果是,modal ViewController当应用程序第一次加载时,a 有一个非常流畅的动画,但最终会出现这种可怕的锯齿状动画,有时只有 1 或 2 帧,后来。同样,有几个相当复杂的计算解除这个 Modal (包括一个 managedObjectContext 保存),它确实接近揭示UITableView下面(意思是 some cellForRowAtIndexPath)但是没有办法单独这两个东西可以使动画虚拟0 帧/秒。所有这一切让我相信资源正在被耗尽并且不会返回。现在可悲的是,我对 iOS 环境的了解还不够,无法确定下一个假设,但这里有:

  • 记忆。简单地说,不可能真的是这样吧?尽管我的应用程序非常占用内存,并且包含大量图像,但我很确定 iOS 会在 RAM 开始用完时将其从 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 数据库时,这段代码可能会执行一次,即不是特别频繁。

4

2 回答 2

4

三个观察:

  1. 你说“很多图像我很确定 iOS 在内存开始用完时会从内存中删除东西”——仅供参考,如果你正在使用imageNamed,那么管理内存并不是很好。当我进行图像缓存时,我使用自己的NSCache. 如果您使用大图像,这一点尤其重要。

  2. 请参阅WWDC 2012 - Building Concurrent User Interfaces on iOS,了解如何使用 Instruments 来确定性能瓶颈所在的实际示例。

  3. 最后,请参阅使用 Instruments 查找泄漏以获取有关如何使用 Instruments 查找泄漏的指南。另外,不要忽视静态分析器,如果您不使用 ARC,或者如果您使用任何 Core Foundation 调用,这很重要。

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


更新:

既然您已经提供了一些源代码,那么让我眼前一亮的一件事addSubview就是rateViewForConfigureCell. 鉴于单元格被重复使用,您将重复将其添加到单元格中。所以,我建议更换:

// 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到您的单元原型中。

于 2013-01-07T18:50:05.873 回答
2

正如怀疑的那样,您在每次配置运行时都向单元格添加一个新的子视图。这部分代码:

// 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];

每次调用的都是分配和添加新视图。经过足够多的循环后,您的单元格将有数十个这样的单元格,这看起来非常正常,并且可能不会占用大量内存,但绝对会降低您的性能,因为合成引擎在滚动时必须将它们全部考虑在内桌子上下。

任何像这样的子视图都应该添加一次,然后在您的 configureCell 方法中重新配置。nil当您从出列单元格返回时添加它们,或者在init...您的单元格子类的方法中,或者在您用于设计单元格的笔尖中添加它们。

你似乎已经在用你的其他子视图(所有这些viewWithTag:调用)做了类似的事情,所以我不确定为什么这个不是这样。

请注意,这可能仍然无法解决您的性能问题 - 如果“远远超过”问题中显示的内容,那么无论如何您都不会获得 60fps - 在之前的单元格中您只能拥有一定数量的子视图性能就消失了。这里似乎发生了很多事情。你说你是一个初学者——也许一个不那么雄心勃勃的介绍项目可能是为了?

于 2013-01-07T20:14:58.683 回答