服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - IOS - 深入分析iOS应用中对于图片缓存的管理和使用

深入分析iOS应用中对于图片缓存的管理和使用

2021-01-14 18:21ForeverYoung21 IOS

这篇文章主要介绍了iOS应用中对于图片缓存的管理和使用,实例代码为传统的Objective-C,需要的朋友可以参考下

我们的 iOS 应用都包含了大量的图像。创建富有吸引力的视图,主要依赖于大量的装饰图片,所有这些首先必须从远程服务器获取。如果每次打开应用都要从服务器一次又一次的获取每个图像,那么用户体验肯定达不到好的效果,所以本地缓存远程图像是非常有必要的。

两种方式加载本地图片
1.通过imageNamed:方法加载图片

用过这种方式加载图片,一旦图片加载到内存中,那么就不会销毁,一直到程序退出。(也就是说imageNamed:会有图片缓存的功能,当下次访问图片的时候速度会更快。)
用这种方式加载图片,图片的内存管理并不受程序员控制。

复制代码 代码如下:

UIImage *image = [UIImage imageNamed: @“image”]


的意思是创建一个UIImage对象,并不是说image这个本身就是一张图片,而是image指向一张图片。在创建这个对象的时候实际上并没有把真正的图片加载到内存里,而是等到用到图片的时候才会加载。
如上例,如果把image对象设置为nil,如果是其它对象,那么没有强指针指向一个对象,这个对象就会销毁;但是即使image = nil,它会指向的图片资源也不会销毁。
2.通过imageWithContentsOfFile:方式加载图片
使用这个方法加载图片,当指向图片对象的指针销毁或指向其它对象,这个图片对象没有其它强指针指向,这个图片对象会销毁,不会一直在内存中停留。

 

因为没有缓存,所以如果相同的图片多次加载,那么也会有多个图片对象来占用内存,而不是用缓存的图片。

使用这个方法,需要file的全路径(之前用NSString, NSArray之类的加载文件也是一样的,比如stringWithContentsOfFile:,看到file就知道是需要传入全路径。)

复制代码 代码如下:

NSString *imagePath = [[NSBundle mainBundle] pathForResource:imageName ofType:@"png"];
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];


注意如果图片在Images.xcassets中,是不能使用这个方法的。所以说想要自己进行图片的内存管理(不希望有缓存图片),那么要将图片资源直接拖入工程,而不是放在Images.xcassets中。

 


快速队列和慢速队列
我们设置了两个队列,一个串行,一个并行。在屏幕上被迫切要求的图片进入并行队列(fastQueue),可能晚点才需要的图片进入串行队列(slowQueue)。
就UITableView的实现而言,这意味着在屏幕上的表格单元从fastQueue获取图片, 每个关闭的屏幕行的图片从slowQueue预加载。

现在不需要处理图片

假设我们要从服务器上请求包含30条事件的一页资讯回来,一旦这些内容请求回来时我们就可以排队等待预取其中的每一张图。

复制代码 代码如下:

- (void)pageLoaded:(NSArray *)newEvents {   
    for (SGEvent *event in newEvents) {       
       [SGImageCache slowGetImageForURL:event.imageURL thenDo:nil];   
    }
}


slowGetImageForURL:这个方法将图片添加到slowQueue这个队列当中,允许它们在不阻塞网络通信的前提下被一张一张的取出来。
thenDo:这个代码块在这里是没有被实现,是因为我们目前还不需要对图片做任何事情。所有我们需要做的就是确保它们在本地磁盘缓存当中,并且随时准备在屏幕上滑动表格时来使用。

 

现在就要处理图片

显示在屏幕上的表格希望立即显示它们的图片,所以在table cell子类当中实现:

复制代码 代码如下:

- (void)setEvent:(SGEvent *)event {   
    __weak SGEventCell *me = self;   
    [SGImageCache getImageForURL:event.imageURL thenDo:^(UIImage *image) {       
       me.imageView.image = image;    }
    ];
}


getImageForURL:这个方法将抓取图片的过程添加到fastQueue这个队列当中,意味着只要iOS系统允许,它们会并行被地执行。如果抓取图片的过程已经存在于slowQueue队列当中,它会被移动到fastQueue队列中,从而避免重复请求。

 

一直异步

等等,getImageForURL:不是一个异步方法吗?如果你明知道图片已经在缓存中,但是却不想在主线程上立即使用它吗?直觉告诉你那是错误的。
从磁盘上加载图片太费资源,同样解压图片也会费很多资源。可以在滑动的过程当中进行配置和添加表格,这最后一件你想在滑动表格时做的事是很危险地,因为它会阻塞主线程,会有卡顿的现象出现。
使用getImageForURL:可以让磁盘加载的动作脱离主线程,于是当thenDo:这个用于收尾工作的代码块执行的时候它已经有了一个UIImage实例,从而不会有滑动卡顿的危险。如果图片已经存在于本地缓存当中,用于收尾工作的代码块会在下一次运行周期执行,并且用户不会注意到两者之间的差别。他们会注意到的是滑动不会卡顿了。

现在,不需要你快速执行

如果用户很快的滑动表格到底部,几十或几百个表格单元会出现在屏幕上,并向fastQueue请求图片数据,然后很快地从屏幕上消失。突然间这个并行地队列会将大量实际上不再需要的图片请求充斥进网络。当用户最终停止滑动时,那些当前屏幕上相应的表格单元视图会将它们的图片请求至于那些并不急需的请求后面,因此网络阻塞了。
这就是 wheremoveTaskToSlowQueueForURL:这个方法的产生的原因.

复制代码 代码如下:

// a table cell is going off screen-
(void)tableView:(UITableView *)table       
didEndDisplayingCell:(UITableViewCell *)cell       
forRowAtIndexPath:(NSIndexPath*)indexPath {   
     // we don't need it right now, so move it to the slow queue            
     [SGImageCache moveTaskToSlowQueueForURL:[[(id)cell event] imageURL]];
}


这确保在fastQueue中的只会有真正需要被快速执行的任务。任何以前认为需要快速执行但现在不需要的任务会被移至slowQueue中。

 

重点和选择

已经有相当多的iOS图片缓存库。它们中一些库只针对某些应用场景,一些库提供了不同场景一定的可扩展性。我们的库即没有专门针对某些应用场景,也没有太多大而全的特性。针对我们的用户我们有三类基本的重点:
重点 1: 最好的帧率
很多的库都非常专注在这一点上,使用一些高度定制和复杂的方法,尽管基准没有决定性地显示这样有效。我们发现最好的帧率由这些决定:
将对磁盘的访问(并且几乎其它的所有)脱离主线程。
使用UIImage的内存缓存来避免不必要的磁盘访问和图片解压。

重点 2: 让最最重要的图片优先显示
大多数的库都考虑让队列管理成为别人关心的事。对于我们的应用,这几乎是最重要的点。
让正确的图片在正确的时间显示在屏幕上可以归结为一个简单的问题:“我们现在就需要它显示还是过一会儿?”。那些需要立即显示的图片是并行加载地,而其它所有东西都被添加到串行队列中。所有之前急迫的事但现在不急迫的话就会从fastQueue分到slowQueue中。并且当fastQueue在工作时,slowQueue是处于挂起状态的。
这让那些急需显示的图片可以单独访问网络,同时也确保了一张非急需显示的图片可以在过一会成为一张急需显示的图片,因为它已经存到了缓存当中,随时准备用于显示。

重点 3: 尽可能简单的API
大多数库都做到了这一点。许多库为了隐藏细节内容而提供了UIImageView的分类,并且许多库让抓取一张图片的流程变得尽可能的便利。针对我们经常做的三件事,我们的库选定了三个主要的方法:
快速抓到一张图

复制代码 代码如下:

__weak SGEventCell *me = self;[SGImageCache getImageForURL:event.imageURL thenDo:^(UIImage *image) {    me.imageView.image = image;}];


排队等待一张我们一会才需要的图片

复制代码 代码如下:

[SGImageCache slowGetImageForURL:event.imageURL thenDo:nil];


通知缓存一张急需显示的图已经不需要立刻显示

复制代码 代码如下:

[SGImageCache moveTaskToSlowQueueForURL:event.imageURL];

 

结论

通过专注于预取,队列管理,从主线程移除耗时的任务,并且依赖于UIImage内置的内存缓存,我们努力从一个简单的软件包中得到好的结果。

延伸 · 阅读

精彩推荐
  • IOSIOS 屏幕适配方案实现缩放window的示例代码

    IOS 屏幕适配方案实现缩放window的示例代码

    这篇文章主要介绍了IOS 屏幕适配方案实现缩放window的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要...

    xiari5772021-06-01
  • IOSiOS中tableview 两级cell的展开与收回的示例代码

    iOS中tableview 两级cell的展开与收回的示例代码

    本篇文章主要介绍了iOS中tableview 两级cell的展开与收回的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    J_Kang3862021-04-22
  • IOS关于iOS自适应cell行高的那些事儿

    关于iOS自适应cell行高的那些事儿

    这篇文章主要给大家介绍了关于iOS自适应cell行高的那些事儿,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的...

    daisy6092021-05-17
  • IOSiOS通过逆向理解Block的内存模型

    iOS通过逆向理解Block的内存模型

    自从对 iOS 的逆向初窥门径后,我也经常通过它来分析一些比较大的应用,参考一下这些应用中某些功能的实现。这个探索的过程乐趣多多,不仅能满足自...

    Swiftyper12832021-03-03
  • IOSIOS开发之字典转字符串的实例详解

    IOS开发之字典转字符串的实例详解

    这篇文章主要介绍了IOS开发之字典转字符串的实例详解的相关资料,希望通过本文能帮助到大家,让大家掌握这样的方法,需要的朋友可以参考下...

    苦练内功5832021-04-01
  • IOSiOS 雷达效果实例详解

    iOS 雷达效果实例详解

    这篇文章主要介绍了iOS 雷达效果实例详解的相关资料,需要的朋友可以参考下...

    SimpleWorld11022021-01-28
  • IOS解析iOS开发中的FirstResponder第一响应对象

    解析iOS开发中的FirstResponder第一响应对象

    这篇文章主要介绍了解析iOS开发中的FirstResponder第一响应对象,包括View的FirstResponder的释放问题,需要的朋友可以参考下...

    一片枫叶4662020-12-25
  • IOSiOS布局渲染之UIView方法的调用时机详解

    iOS布局渲染之UIView方法的调用时机详解

    在你刚开始开发 iOS 应用时,最难避免或者是调试的就是和布局相关的问题,下面这篇文章主要给大家介绍了关于iOS布局渲染之UIView方法调用时机的相关资料...

    windtersharp7642021-05-04