【问题标题】:UIImageView performance within UITableViewCell using ASIHTTPRequest使用 ASIHTTPRequest 在 UITableViewCell 中的 UIImageView 性能
【发布时间】:2012-03-11 22:36:26
【问题描述】:

这个问题困扰了我一天多一点。要点是我有一个分组 UITableView 设置,这样每个部分只有一个条目。表格视图中的每个单元格都包含一个 UIImageView,它占据了单元格的整个宽度和长度。此外,每个单元格都处于指定的高度和宽度。

这是我的初始化方法:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
      [[self layer] setMasksToBounds:NO];
      [[self layer] setShouldRasterize:YES];
      [[self layer] setRasterizationScale:[[UIScreen mainScreen] scale]];
      [[self layer] setCornerRadius:10];

      _bgCoverView = [[UIImageView alloc] initWithFrame:CGRectZero];
      [_bgCoverView setClipsToBounds:YES];
      [[_bgCoverView layer] setCornerRadius:10];
      [_bgCoverView setContentMode:UIViewContentModeScaleAspectFill];
      [self.contentView addSubview:_bgCoverView];
      [_bgCoverView release];

      //Init other parts of the cell, but they're not pertinent to the question
    }
    return self;
}

这是我的布局子视图:

- (void)layoutSubviews{
  [super layoutSubviews];

  if (!self.editing){

    [_bgCoverView setFrame:CGRectMake(0, 0, tableViewCellWidth, self.contentView.frame.size.height)];
  }
}

这是我的单元格设置器:

- (void)setGroup:(Group*)group{
  //Set background cover for the contentView
  NSSet *usableCovers = [[group memories] filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"pictureMedData != nil || pictureFullData != nil || (pictureMedUrl != nil && NOT pictureMedUrl contains[cd] '/missing.png')"]];
  Memory *mem = [usableCovers anyObject];

  [_bgCoverView setImage:[UIImage imageWithContentsOfFile:@"no_pics_yet.png"]];
  if (mem != nil){
    [Folio cacheImageAtStringURL:[mem pictureMedUrl] forManagedObject:mem withDataKey:@"pictureMedData" inDisplay:_bgCoverView];
  }
}

这就是我认为我正在获得表演的地方。首先,有过滤的集合调用。如果集合中有很多对象,这可能会减慢速度。但是,对象往往相对较少。此外,当我删除这行代码时,我没有看到性能变化,所以这不太可能。

所以,我的缓存方法(位于辅助类中)几乎没有了。方法如下:

+(void)cacheImageAtStringURL:(NSString *)urlString forManagedObject:(NSManagedObject*)managedObject withDataKey:(NSString*)dataKey inDisplay:(NSObject *)obj{
  int objType = 0;        //Assume UIButton
  if (obj == nil)
    objType = -1;
  else if ([obj isKindOfClass:[UIImageView class]])
    objType = 1;          //Assign to UIImageView

  NSData *data = (NSData*)[managedObject valueForKey:dataKey];
  if ([data bytes]){
    if (objType == 0)       [(UIButton*)obj setBackgroundImage:[UIImage imageWithData:data] forState:UIControlStateNormal];
    else if (objType == 1)  [(UIImageView*)obj setImage:[UIImage imageWithData:data]];
    return;
  }

  //Otherwise, do an ASIHTTPRequest and set the image when it's returned, save the data into core data so that we get a cache hit the next time.
}

无论图像是否被缓存,在此视图上向下滚动都会出现重大的性能问题。如果我注释掉缓存方法,它工作得很好。此外,调用此方法的其他视图很迟钝,所以我很确定它在这里。

不过,我还要说,我也对cornerRadius 代码持怀疑态度,但是,我听说如果将 shouldRasterize 设置为 YES,它会导致性能加速。不过,我不确定这是否 100% 正确,或者我的实现是否已关闭。

任何帮助将不胜感激。


更新

尚未 100% 修复,但我们正在实现目标。

这些变量必须在请求中设置:

[request setCachePolicy:ASIOnlyLoadIfNotCachedCachePolicy];
[request setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];

第一个告诉缓存仅在图像未缓存时对服务器执行 ping 操作。第二个告诉缓存在应用程序的生命周期内永久存储图像。

这让我半途而废。一些图像立即加载,而另一些则显得迟缓。我做了更多的研究并在我的应用程序委托中添加了这些代码行:

[[ASIDownloadCache sharedCache] setShouldRespectCacheControlHeaders:NO];
[ASIHTTPRequest setDefaultCache:[ASIDownloadCache sharedCache]];

第一行至关重要。 ASIHTTPRequest 的默认操作是遵循 Web 服务器告诉您的内容。因此,如果它在其标头中告诉您不要缓存响应,它不会。这条线覆盖了它。现在,这在我的 iOS 模拟器上运行得非常好。然后在我的4S上试了一下,又慢了。

原因是我在请求中设置了一段代码,当成功检索到图像后,我会将其保存到 Core Data。每次都会触发此调用,即使对于缓存的请求也是如此。

这是因为 ASIHTTPRequest 仍然会发出请求,无论它是否使用数据的缓存副本。如果它有一个缓存副本,它不会向服务器发送请求,而是用缓存数据填充其请求对象的响应数据。因此,在我的块负责显示工作之后,我调用:

if ([request didUseCachedResponse])   return;

这在 4S 和 4 上效果很好。但是,3GS 仍然非常慢:-(。

这是因为 ASIHTTPRequest 使用的是我的 NSData 而不是我的 UIImage 的缓存副本。因此,每次使用缓存时都需要重新创建图像。这是有道理的,因为 ASIHTTPRequest 只返回数据,并且不知道之后您将如何处理数据,因此它总是会调用您在返回请求时设置的代码。而且因为我的代码块是从数据中转换图像,所以我得到了。

前进的道路

我认为要获得我想要的性能加速,有必要实现自定义 UIImage 缓存。如果我们在缓存中找到文件,我们返回 UIImage 对象。如果没有,那么我们创建一个请求并在返回时将其保存在缓存中。

【问题讨论】:

    标签: iphone ios caching uitableview uiimageview


    【解决方案1】:

    shouldRasterize 的提示是有效的,但根据我的经验,这会显着降低图形质量。此外,由于像 iOS4.3 左右(不记得了)他们极大地改进了 cornerRadius 调用,自从我上次需要为此类问题激活光栅化以来已经很长时间了。 您可以随时停用 cornerRadius 并查看它的执行情况。

    我真正想告诉你的是,ASIHTTPRequest 具有内置的缓存支持,请参阅

    http://allseeing-i.com/ASIHTTPRequest/How-to-use#using_a_download_cache

    试试吧,它对我来说完美无缺。考虑哪些缓存策略最适合您的需求。当然,如果您需要 CoreData 中的图像,则必须插入它们,但我不会尝试从 CoreData 手动缓存它们。

    哦,还有别的东西...... ASIHTTPRequest 不再处于开发阶段,您可能需要考虑切换到另一个框架(如 AFNetworking)。 ASIHTTPRequest 例如不支持 ARC。

    【讨论】:

    • 这是非常棒的信息。老实说,我不知道 ASIHTTPRequest 没有在开发中。在我的应用程序的第一个版本之后,我肯定必须改变我的框架。至于缓存,我有一个全局下载缓存集,但它只是有点帮助。大部分问题在于在 UITableViewCell 中实际呈现 UIImage 所需的时间。
    • 是的,因为您总是使用imageWithData: 初始化 UIImage。滚动会延迟加载,因此每次单元格可见时,您都会再次调用imageWithData:。这是非常低效的。因此,您可以使用 ASIHTTPRequest 的缓存来为您存储图像本身(不是 NSData),也可以缓存实际的 UIImage 对象。如果您确实需要在每次用户滚动时重新创建每个图像(我对此表示怀疑),您可以使用 ElJay 对 GCD 的回答。
    • 我认为在这种情况下我需要缓存实际的 UIImage 对象。当我把它留给 ASIHTTPRequest 时,加载图像会有一点延迟,这是不可接受的。滚动速度大大加快,但视图更糟。但是,当我修改 ElJay 将 NSData 存储在我的模型中的方法时,没有图像加载,但仍然有些滞后。现在我知道这是因为存在与 imageWithData 相关的开销。我要做的是将 UIImage 存储在我的核心数据模型中,这样我就不必继续调用 imageWithData。
    • 其实既然 Core Data 必须把东西变成 NSData,那这种方法会有效吗?
    • 在 UITableView 的情况下,没有。正如我所说,UITableView 实现了延迟加载(你绝对应该使用这个功能!),所以它不能很好地与 CoreData 的图像存储配合使用。我只会将 UIImage 对象缓存在(即单例)NSDictionary 中并按需检索它们。当然,这会占用大量内存(取决于图像的大小),因此请注意这一点。顺便说一句,如果正确实施,ASIHTTPRequest 不会减慢您的速度。它会加载一次图像(无论如何你都必须这样做),但在那之后,图像会立即出现。使用正确的缓存策略!
    【解决方案2】:

    查看this post。我使用块和 GCD 来加载图像(在 TableViewCell 中特别有用)。如果您将方法调用放在 cellForRowAtIndexPath 方法中,这会即时加载图像。我没有迟钝的用户界面。

    【讨论】:

      猜你喜欢
      • 2013-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-07
      • 1970-01-01
      相关资源
      最近更新 更多