常用的UIImage加载图片的方式有以下几种:
[UIImage imageNamed:name]
[UIImage imageWithContentsOfFile:name]
[UIImage imageWithData:data]
[UIImage imageWithCGImage:imageRef]
[UIImage imageWithCIImage:obj]
以上方法中imageNamed:
和imageWitData:
应用程序会对其自动缓存,不过,它们缓存的实现方式并不相同,后面会有详细说明。除去这两个方法以外剩下的三个方法默认情况下是不会产生缓存的,这三个方法的主要区别在于它们的数据源:imageWithContentsOfFile:
从指定文件中创建对象,imageWithCGImage:
以CGImageRef来创建对象,imageWithCIImage:
以CIImage对象来创建对象。
最后两种方法平时用的相对会少一些,简要解释一下CGImage与CIImage的区别:CGImageRef只能代表位图,如果你需要与bitmap数据打交道,无疑CGImage是非常合适的选择。CGImageRef以CG开头就不难想到CGImageRef的相关操作都需要在Core Graphics中进行,比如混合、遮罩等等。CIImage以CI开头,即Core Image,不难理解CIImage是底层的数据对象,它通常包含了与它相关的图像数据,而不是一个图像。默认情况下,CIImage对象是不会被绘图系统渲染的,除非是得到明确的指令。这种机制(“lazy evaluation”)允许核心绘图系统尽可能高效地运行。CIImage通常被运用在GPU优化图像滤镜算法当中。
UIImage缓存原理
1.[UIImage imageNamed:name]
我通过查看imageNamed
方法的调用栈以及查阅苹果相关文档后对UIImage的缓存实现有了清晰的认识。
当调用imageNamed方法时,该方法会去内存缓存里去查找与参数一致的image对象并且返回最合适大小的image对象,如果没有找到,该方法则会去本地磁盘中查找然后加载图片并返回image对象,同时将image对象缓存到系统缓存中,以便下次重复使用。更底层一点的解释,当返回image对象时,并未对image的图片数据进行解码。它的解码过程发生在UIImage对象第一次显示到屏幕上的时候,而image对象的缓存也发生在这时候。当解码完成image显示在屏幕上后,应用程序会将image的解码结果保存到缓存中。通常缓存会在收到内存警告时才会被清空。
2.[UIImage imageWithData:data]
在查看imageWithData方法的调用栈时发现了有意思的东西。通过二进制数据创建image对象时,实际上在底层调用的是ImageIO/ImageIO.h
的CGImageSourceCreateWithData()
方法。该方法的第二个参数可以传入key为kCGImageSourceShouldCache的键值对,它的值是CFBooleanRef类型的,默认情况下,在64位机器上它的值为kCFBooleanTrue,而在32位机器上它的值为kCFBooleanFalse。也就是说在64位机器上是会缓存的,而在32位机器上则是不会缓存的。与imageNamed
方法类似,图片会在第一次显示到屏幕上时才会进行解码,随后再被缓存到CGImage里面。依据是CGImageSourceCreateWithData()
方法第二个参数可以传入key为kCGImageSourceShouldCacheImmediately的键值对,默认情况下它的值是kCFBooleanFalse。
UIImage不缓存
[UIImage imageWithContentsOfFile:name]
与前面两个方法略有不同,该方法是同步的(synchronous)。当在主线程(UI)中调用该方法时,会阻塞主线程并从磁盘中加载图片数据,若磁盘数据较大会造成卡顿或者延迟。通常的解决方法是另开一个线程异步完成磁盘加载图片数据的任务,然后在主线程中刷新UI。下面代码给出了一个例子:
|
|
当图片显示在屏幕上时,系统并不会对其进行缓存。当图片数据被加载到内存中,它会被标记为可清除(purgeable)。如果数据被清除了且需要再次加载,image对象会再次从指定的文件路径获取数据并加载进内存中。根据它的实现原理,通常该方法的使用场景是图片不需要重复展示,或者图片的数据较大会造成内存警告。当然,这并不是说imageWithContentsOfFile:
一定比imageNamed:
方法高效,imageWithContentsOfFile:
方法的问题在于它会将图片文件全尺寸展示在屏幕上即使是512*512的图片都要占到1M多的内存,加载会影响程序的性能,造成不好的用户体验。
针对图片尺寸太大带来的性能问题,比较好的解决方案是用CGImageSource
的有关方法,将图片的尺寸缩减到适合的尺寸,减小数据大小。