繁体   English   中英

使用Xcode资产目录进行UIImage缓存

[英]UIImage caching with Xcode Asset Catalogs

我们都知道UIImage的imageNamed:方法的神秘的幕后缓存机制。 在Apple的UIImage类参考中,它说:

在内存不足的情况下,可以从UIImage对象中清除图像数据以释放系统上的内存。 此清除行为仅影响UIImage对象内部存储的图像数据,而不影响对象本身。 当您尝试绘制其数据已被清除的图像时,图像对象会自动从其原始文件重新加载数据。 然而,这个额外的负载步骤可能会导致很小的性能损失。

实际上,正如文档所示,图像数据不会“从UIImage对象中清除以释放系统上的内存”。 相反,应用程序会收到内存警告,直到它“因内存压力”退出。
编辑:在Xcode项目中使用传统的图像文件引用时,UIImage缓存工作正常。 只是当您转换到资产目录时,内存永远不会被释放。

我实现了一个带有几个UIImageViews的UIScrollView来滚动浏览一长串图像。 滚动时,正在加载下一个图像并将其分配给UIImageView的image属性,删除之前保存的UIImage的强链接。

由于imageNamed:的缓存机制,我很快就会耗尽内存,应用终止时分配了大约170 MB的内存。

当然,有很多有趣的解决方案可以实现自定义缓存机制,包括覆盖类别中的imageNamed: class方法。 通常,使用不缓存图像数据的类方法imageWithContentOfFile:正如Apple开发人员在WWDC 2011上所建议的那样。

这些解决方案适用于常规图像文件,但您必须获得不像我希望的那样优雅的路径和文件扩展名。

我使用Xcode 5中引入的新资产目录 ,以利用根据设备和高效图像文件存储有条件地加载图像的机制。 截至目前,似乎没有直接的方法从资产目录加载图像而不使用imageNamed: ,除非我错过了一个明显的解决方案。

你们是否已经找到了资产目录的UIImage缓存机制?

我想在UIImage上实现一个类似于以下内容的类别:

static NSCache *_cache = nil;

@implementation UIImage (Caching)

+ (UIImage *)cachedImageNamed:(NSString *)name {
    if (!_cache) _cache = [[NSCache alloc] init];

    if (![_cache objectForKey:name]) {
        UIImage *image = ???; // load image from Asset Catalog without internal caching mechanism
        [_cache setObject:image forKey:name];
    }

    return [_cache objectForKey:name];
}

+ (void)emptyCache {
    [_cache removeAllObjects];
}

@end

当然,更好的方法是更好地控制UIImage的内部缓存,以及在使用资产目录时文档中所述的低内存条件下清除图像数据的可能性。

感谢您的阅读,我期待您的想法!

更新:缓存逐出罚款(至少从iOS 8.3开始)。

我遇到了同样的问题(iOS 7.1.1),我觉得@Lukas可能是对的

这个错误很可能不在Apple的......缓存中,而是在你的..代码中。

因此,我写了一个非常简单的测试应用程序(查看下面的完整源代码),我仍然看到了这个问题。 如果您发现任何问题 ,请让我知道。 我知道这取决于图像尺寸。 我只在iPad Retina上看到这个问题。

  @interface ViewController ()

  @property (nonatomic, strong) UIImageView *imageView;
  @property (nonatomic, strong) NSArray *imageArray;
  @property (nonatomic) NSUInteger   counter;

  @end

  @implementation ViewController

  - (void)viewDidLoad
  {
      [super viewDidLoad];

      self.imageArray = @[@"img1", ...  , @"img568"];
      self.counter = 0;

      UIImage *image = [UIImage imageNamed:[self.imageArray objectAtIndex:self.counter]];
      self.imageView = [[UIImageView alloc] initWithImage: image];
      [self.view addSubview: self.imageView];

      [self performSelector:@selector(loadNextImage) withObject:nil afterDelay:1];
  }

  - (void)didReceiveMemoryWarning
  {
      [super didReceiveMemoryWarning];
      NSLog(@"WARN: %s", __PRETTY_FUNCTION__);
  }


  - (void)loadNextImage{

      self.counter++;
      if (self.counter < [self.imageArray count])
      {
          NSLog(@"INFO: %s - %lu - %@",
                __PRETTY_FUNCTION__,
                (unsigned long)self.counter,
                [self.imageArray objectAtIndex:self.counter]);
          UIImage *image = [UIImage imageNamed:[self.imageArray objectAtIndex:self.counter]];
          self.imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height);
          [self.imageView setImage:image];

          [self performSelector:@selector(loadNextImage) withObject:nil afterDelay:0.2];
      } else
      {
          NSLog(@"INFO: %s %@", __PRETTY_FUNCTION__, @"finished");
          [self.imageView removeFromSuperview];
      }
  }
  @end

就地实施

我写了一些代码来保存图像资产,但用imageWithData:imageWithContentsOfFile加载它: 使用没有imageNamed的xcassets来防止内存问题?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM