草庐IT

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

coder 2024-01-10 原文

似乎在使用了一段时间后,我的应用程序在 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/

有关iphone - UITableView 无缘无故变得缓慢且无响应的更多相关文章

  1. 网站日志分析软件--让网站日志分析工作变得更简单 - 2

    网站的日志分析,是seo优化不可忽视的一门功课,但网站越大,每天产生的日志就越大,大站一天都可以产生几个G的网站日志,如果光靠肉眼去分析,那可能看到猴年马月都看不完,因此借助网站日志分析工具去分析网站日志,那将会使网站日志分析工作变得更简单。下面推荐两款网站日志分析软件。第一款:逆火网站日志分析器逆火网站日志分析器是一款功能全面的网站服务器日志分析软件。通过分析网站的日志文件,不仅能够精准的知道网站的访问量、网站的访问来源,网站的广告点击,访客的地区统计,搜索引擎关键字查询等,还能够一次性分析多个网站的日志文件,让你轻松管理网站。逆火网站日志分析器下载地址:https://pan.baidu.

  2. ruby - Puma 服务器无故死机 - 2

    我正在运行Puma2.8.2服务器来stub我的一些后端服务。有时Puma服务器会无缘无故地死掉。error.log中没有错误,下面是access.log的摘录:10.210.140.21--[15/Oct/201409:28:22]"GET/statusHTTP/1.1"200-0.0030-Gracefullystopping,waitingforrequeststofinish-Goodbye!===pumastartup:2014-10-1509:28:24+0100===10.210.140.21--[15/Oct/201409:28:24]"GET/statusHTTP/1

  3. ruby-on-rails - 即使没有挂起的迁移,Rails 迁移也非常缓慢 - 2

    我的生产Rails应用程序需要167秒来运行rakedb:migrate。可悲的是,没有要运行的迁移。我试图在检查是否有待处理的迁移时调整运行的迁移,但随后检查花费了同样长的时间。我心目中唯一的“借口”是数据库并不小,那里有1M条记录,但我看不出这有什么关系。我查看了日志,但没有任何迹象表明出了什么问题。我在运行ruby2.2.0rails4.2.0有没有人知道为什么会这样,是否有什么办法可以解决? 最佳答案 运行rakedb:migrate任务还会调用db:schema:dump任务,这将更新您的db/schema.rb。因此,即

  4. ruby - Net::HTTP 对 HTTPS 请求的响应极其缓慢 - 2

    出于某种原因,在我的开发机器上,我对通过Net::HTTP执行的HTTPS请求的响应非常非常慢。我试过RestClient和HTTParty,它们都有同样的问题。它似乎是凭空冒出来的。我已毫无问题地提出这些请求数百次,但今天它们的速度慢得令人难以忍受。pry(main)>putsTime.now;HTTParty.get('https://api.easypost.com/v2/addresses');putsTime.now;2015-04-2908:07:08-05002015-04-2908:09:39-0500如您所见,响应耗时2.5分钟。不仅仅是这个EasyPostAPIUR

  5. ruby - 为什么 split (' ' ) 试图变得(太)聪明? - 2

    我刚刚发现String#split有以下奇怪的行为:"a\tbc\nd".split=>["a","b","c","d"]"a\tbc\nd".split('')=>["a","b","c","d"]"a\tbc\nd".split(//)=>["a\tb","c\nd"]Thesource(来自2.0.0的string.c)超过200行,包含这样一段话:/*L5909*/elseif(rb_enc_asciicompat(enc2)==1){if(RSTRING_LEN(spat)==1&&RSTRING_PTR(spat)[0]==''){split_type=awk;}}后来,在

  6. iphone - 扩展 restful_authentication/AuthLogic 以支持匿名 iPhone 的延迟登录的最佳方法是什么? - 2

    我正在构建一个与RubyonRails后端对话的iPhone应用程序。RubyonRails应用程序还将为Web用户提供服务。restful_authentication插件是提供快速和可定制的用户身份验证的绝佳方式。但是,我希望iPhone应用程序的用户在新列中存储一个由手机的唯一标识符([[UIDevicedevice]uniqueIdentifier])自动创建的帐户。稍后,当用户准备好创建用户名/密码时,帐户将更新为包含用户名和密码,iPhone唯一标识符保持不变。用户在设置用户名/密码之前不能访问该网站。然而,他们可以使用iPhone应用程序,因为该应用程序可以使用它的标识符

  7. ruby - 缓慢的 Ruby 正则表达式通过奇怪的变化变得快速 - 2

    我一直在调试网站以查找页面加载时间过长的根源,并将其缩小为用于从文本中提取URL的正则表达式:/(?:([\w+.-]+):\/\/|(?:www\.))[^\s在一大块文本上运行大约需要3秒。我发现如果我将第一个子句的逆语句添加到正则表达式((?:[^\w+.-]|^))的开头,它几乎会立即运行:/(?:[^\w+.-]|^)(?:([\w+.-]++):\/\/|(?:www\.))[^\s在我看来,添加的子句根本不应该影响正则表达式,因为没有什么可以导致该子句失败(因为这些字符将与“[\w+.-]++”子句匹配)。为什么这会使正则表达式运行得更快?编辑有些人要求提供我正在尝试做的

  8. ruby - 只是为了好玩,我如何编写一个 ruby​​ 程序,一次缓慢地打印到 stdout 一个字符? - 2

    我认为这可能有效"abcdefghijk".each{|c|putcc;sleep0.25}我希望看到“abcdefj”一次打印一个字符,每个字符之间间隔0.25秒。但是整个字符串是一次打印出来的。 最佳答案 两件事:您需要使用.each_char来遍历字符。在Ruby1.8中,String.each将逐行进行。在Ruby1.9中,String.each已弃用。如果您希望字符立即出现,您应该手动刷新$stdout。否则,它们往往会被缓冲,以便字符在最后一次出现。.#!/usr/bin/envruby"abcddefghijk".ea

  9. ruby-on-rails - REXML::RuntimeError(实体扩展变得太大) - 2

    今天升级到Ruby-1.9.3-p392后,REXML在尝试检索超过一定大小的XML响应时抛出运行时错误-一切正常,当接收到25条以下的XML记录时不会抛出错误,但是一旦达到特定的XML响应长度阈值,我收到此错误:Erroroccurredwhileparsingrequestparameters.Contents:RuntimeError(entityexpansionhasgrowntoolarge):/.rvm/rubies/ruby-1.9.3-p392/lib/ruby/1.9.1/rexml/text.rb:387:in`blockinunnormalize'我意识到这在最

  10. iphone - 设计和 Rails 3 中的 http 身份验证 - 2

    我有一个使用deviseonrails3的应用程序。我想启用http身份验证,以便我可以从iPhone应用程序向我的网络应用程序进行身份验证。如何从我的iPhone应用程序进行身份验证以进行设计?这安全吗?还是我应该进行不同的身份验证? 最佳答案 从设计的角度来看,您有3个选择:1)使用基本的http身份验证:您的iPhone应用程序有一个secretkey-这是在您的iPhone应用程序代码中烘焙的-用于对网络应用程序的每个请求进行身份验证。Google搜索:“设计基本的http身份验证”2)您可以通过在您的iPhone应用程序中

随机推荐