似乎在稍微使用后,我的应用程序在UITableViewController
上变得缓慢且无响应。有一些非常密集的方法,但一旦这些方法完成,没有理由他们应该影响应用程序的整体性能。
我一直在使用工具来确定它可能是什么,但它证明非常不确定。首先,我无法通过一种方法重新创建问题,它似乎只是出现在一般用法中。根据线程使用情况,使用CPU的主要内容是我的cellForRowAtIndexPath
。虽然我确实做了很多计算,但它并没有解释两件事。 1)为什么这个问题会随着时间而发展。当我第一次启动应用程序时,我可以多次向上和向下滚动TableViewController
,一切都非常流畅。 2)即使当我除去3或4个细胞以外的所有细胞时,仍然没有反应。
另一个观察是,modal ViewController
在首次加载应用时动画非常流畅,但最终会出现这种可怕的锯齿状动画,有时只有1或2帧。同样,对这个Modal的解雇还有一些相当复杂的计算(包括一个managedObjectContext保存),并且它确实接近显示下面的UITableView
(意思是一些cellForRowAtIndexPath
)但是没有办法仅这两件事就可以将动画带到几乎0 fps。所有这些都让我相信资源已经用光而且没有回归。现在可悲的是,我对iOS环境的了解不足以确定下一个假设,但这里有:
存储器。简单地说,它真的可以这样吗?尽管我的应用程序内存非常密集且有大量图像,但我很确定iOS会在RAM开始耗尽时将其删除。最重要的是,诊断显示我在应用程序中遇到大量延迟时已经有50MB的空闲时间。
CPU。我认为可能有些东西耗尽我的所有CPU,就像上次遇到性能问题时(我称之为无限循环的方法,哎呀)但是,应用程序似乎只在需要时才使用CPU。换句话说,当应用程序空闲时,它会回到0使用状态。只是为了解释这是多么重要;如果CPU作为一种资源被某种东西吃掉,那么它就可以解释为什么cellForRowAtIndexPath
随着时间的推移而挣扎。但是,由于它没有被吃掉,因此没有理由长时间使用该应用程序会导致cellForRowAtIndexPath
使用过多的CPU。因此,我无法看到我的问题是由CPU导致的。
当然,因为这些是我能想到的唯一两种资源,我完全彻底地难以接受。我不知道为什么应用程序会随着时间的推移变慢。实际上,我无法看到它是如何成为代码的,因为应用程序在首次启动时工作正常,而且当看起来有足够的剩余时我无法看到它是如何成为资源的。
由于我是Objective-C / iOS开发的新手,我怀疑还有一些我不知道的东西。如果你能帮我识别它,我真的很感激。
更新:发布我的cellForRowAtIndexPath
和willDisplayCell
代码:
- (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:使用Analyzer工具找到了一些东西。我不完全确定它是如何泄漏的,但无论如何都要进行。
SearchResult *searchResult = [[SearchResult alloc] init];
for (id i in fetchedResultsController.fetchedObjects) {
MyEntity *myEntity = i;
searchResult = [searchResults objectForKey:myEntity.id];
每次将新实体添加到Core Data数据库时,这段代码可能会被执行一次,即不是特别经常。
答案 0 :(得分:4)
三个观察结果:
你说“很多图片我很确定iOS会在内存耗尽时从RAM中删除” - 仅供参考,如果你使用的是imageNamed
,那就不太好了管理你的记忆。当我进行图像缓存时,我使用自己的NSCache
。如果您使用大图像,这一点尤其重要。
有关如何使用工具识别性能瓶颈的实际示例,请参阅WWDC 2012 - Building Concurrent User Interfaces on iOS。
最后,请参阅Finding leaks with Instruments以获取有关如何使用仪器查找泄漏的指导。另外,请不要忽略static analyzer,如果您不使用ARC,或者使用任何Core Foundation调用,这一点很重要。
除此之外,如果不看代码,很难找出问题的根源。
<强>更新强>
既然您已经提供了一些源代码,那么对我来说就是addSubview
的{{1}}。鉴于细胞被重复使用,您将重复添加细胞。所以,我建议更换:
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];
添加到您的手机原型。
答案 1 :(得分: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...
方法中,或者在用于设计单元格的nib中添加它们。
您似乎已经在使用其他子视图(所有这些viewWithTag:
来电)做了类似的事情,所以我不确定为什么不是这种情况。
请注意,这可能仍然无法解决您的性能问题 - 如果有更多&#34;更多&#34;比你在问题中所显示的那样,你无论如何都不会获得60fps - 在演出结束之前,单元格中只能有一定数量的子视图。这里似乎有很多事情发生。你说你是初学者 - 也许一个不太雄心勃勃的引进项目可能是有序的?