UIImage 的本地保存
保存 UIImage 有三种方式:
- 直接使用 NSKeyedArchiver,把 UIImage 序列化保存
- 用
UIImagePNGRepresentation()
把图片转为 PNG 保存 - 用
UIImageJPEGRepresentation()
把图片压缩成 JPEG 保存。
其中,方法一也是调用了 UIImagePNGRepresentation
进行序列化,用它保存图片的消耗是最大的。苹果对 JPEG 有硬编码和硬解码,保存成 JPEG 会大大缩减编码解码时间,也能减小文件体积。所以如果图片不包含透明像素时,UIImageJPEGRepresentation(0.9)
是最佳的图片保存方式,其次是 UIImagePNGRepresentation()
。
UIImage 的 缓存
通过 imageNamed 创建 UIImage 时,系统实际上只是在 Bundle 内查找到文件名,将其放到 UIImage 里返回。当显示在屏幕上时,内部的解码方法才会被调用,并且会将解码的结构保存在一个全局缓存里,并且会长期存在(除非收到内存警告)。也就是说imageNamed:会有图片缓存的功能,当下次访问图片的时候速度会更快。
用这种方式加载图片,图片的内存管理并不受程序员控制。
如果把image对象设置为nil,如果是其它对象,那么没有强指针指向一个对象,这个对象就会销毁;但是即使image = nil,它会指向的图片资源也不会销毁。
使用 imageWithData 能不能避免缓存?
答案是不能,通过数据创建 UIImage 时,UIImage 底层是调用 ImageIO 的 CGImageSourceCreateWithData()
方法。该方法有个参数叫 ShouldCache,在 64 位的设备上,这个参数是默认开启的。这个图片也是同样在第一次显示到屏幕时才会被解码,随后解码数据被缓存到 CGImage 内部。与 imageNamed 创建的图片不同,如果这个图片被释放掉,其内部的解码数据也会被立刻释放。
避免缓存的方法
- 手动调用
CGImageSourceCreateWithData()
创建图片,并关掉ShouldCache
和ShouldCacheImmediately
。但是这种方式会导致每次图片显示到屏幕时,解码方法都会被调用,造成很大的 CPU 占用。 - 把图片用
CGContextDrawImage()
绘制到画布上,然后把画布的数据取出来当作图片。
几种方式加载图片
- 第一种就是上面说的
imageNamed:
方法加载图片,因为有缓存功能,所以再次访问速度更快。具体可以看上面。 - 通过
imageWithContentsOfFile:
方式加载图片:- 使用这个方法加载图片,当指向图片对象的指针销毁或指向其它对象,这个图片对象没有其它强指针指向,这个图片对象会销毁,不会一直在内存中停留。
- 因为没有缓存,所以如果相同的图片多次加载,那么也会有多个图片对象来占用内存,而不是用缓存的图片。
- 使用这个方法,需要file的全路径(之前用NSString, NSArray之类的加载文件也是一样的,比如stringWithContentsOfFile:,看到file就知道是需要传入全路径。)
1
2NSString *imagePath = [[NSBundle mainBundle] pathForResource:imageName ofType:@"png"];
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
注意如果图片在Images.xcassets中,是不能使用第二种方法的。所以说想要自己进行图片的内存管理,那么要将图片资源直接拖入工程,而不是放在Images.xcassets中。
PNG 与 JPEG 的区别
png是一种使用无损压缩的图片格式,而大家熟知的另外一种图片格式——jpeg则是采用有损压缩的方式。
用通俗易懂的方式来讲,当原图片数据被编码成png格式后,是可以完全还原成原本的图片数据的,而编码成jpeg则会损耗一部分图片数据,这是因为两者的编码方式和定位不同。jpeg着重于人眼的观感,保留更多的亮度信息,去掉一些不影响观感的色度信息,因此是有损耗的压缩。png则保留原始所有的颜色信息,并且支持透明/alpha通道,然后采用无损压缩进行编码。因此对于jpeg来说,通常适合颜色更丰富、可以在人眼识别不了的情况下尽可能去掉冗余颜色数据的图片,比如照片之类的图片;而png适合需要保留原始图片信息、需要支持透明度的图片。