日常总结
1:自己设计一个图片缓存框架需要考虑的问题.
1>命名问题(url_时间戳的方式)
为什么命名要加上时间戳,是因为我们缓存图片不可能无休止的去缓存,这样会造成我们的app占用的内存过大,时间戳是为了记住我们图片缓存的时间.例如SDWebImage的做法,缓存超过一周的清除掉.
2>weak 和 unowned的问题,也就是我们说的循环引用的问题.
在避免循环引用的时候,如果某些引用没有值,建议使用weak.如果引用总有值,那就说那个unowned.
例如YYWebImage的做法,获取图片之后的回调里用的是 weak ,因为有可能图片加载完了但是 UIImageView 已经销毁了,然后在查询本地缓存的时候,用的是 unowned ,因为这里的 self 是单例,永远不会销毁.
3> 下载图片的问题.
对于图片下载问题,可以分为两种
1)本地下载(已经有缓存了),实际操作就是已经做过一些编解码的问题, 我们只需要去访问一个沙盒的url去加载图片就可以了.
2)网络下载.
每个图片的下载都由一个Operation操作来完成,并将这些操作放到一个操作队列中。这样可以实现图片的并发下载.
需要创建一个字典(key为url value为操作)去存储operation,将当前下载操作添加到下载操作缓存中 (为了解决重复下载).
4> 下载操作缓存(这里注意,操作缓存时需要加锁)
我们需要在下载回调完成后,立即将当前的下载操作从下载操作缓存中删去!
因为要避免下载失败后,无法再次下载的情况的发生!
为什么呢?
注意一下将下载操作加入到下载操作缓存的时机:
是在下载开始的那一刻而不是下载成功的那一刻!
如果在下载开始的那一刻加入到缓存中的话,这个缓存信息就包括两个情况:下载成功和下载失败:
如果未来下载成功了,那么我们就不会来到判断当前下载操作是否在下载操作缓存这一步,在这之前直接就可以拿图去用了,下载操作是否存在下载操作缓存里并没有什么影响。
但是!如果未来下载失败了,那就肯定不会有对应的图片缓存和沙盒缓存,也就肯定会来到判断当前的下载操作是否在下载操作缓存里这一步。不幸的是,因为没有被删去,它是存在的。存在的话就不做任何其他操作,放任自流,导致曾经下载失败的图片永远不会再次下载。
NSBlockOperation *operation = self.operations[imageUrl];
if (operation) return;//转身就走,毫不留情
因此,无论下载成功或是失败,在图片下载的回调里都要将当前的下载操作从下载操作队列中移走:用来保证如果下载失败了,就可以重新开启对应的下载操作进行下载.
5> 图片变形的解决.
6> 图片存储的格式问题(webP格式图片的处理,gif动图的处理)
将 UIImage 保存到磁盘,用什么方式最好?
目前来说,保存 UIImage 有三种方式:1.直接用 NSKeyedArchiver 把 UIImage 序列化保存,2.用 UIImagePNGRepresentation() 先把图片转为 PNG 保存,3.用 UIImageJPEGRepresentation() 把图片压缩成 JPEG 保存。
实际上,NSKeyedArchiver 是调用了 UIImagePNGRepresentation 进行序列化的,用它来保存图片是消耗最大的。苹果对 JPEG 有硬编码和硬解码,保存成 JPEG 会大大缩减编码解码时间,也能减小文件体积。所以如果图片不包含透明像素时,UIImageJPEGRepresentation(0.9) 是最佳的图片保存方式,其次是 UIImagePNGRepresentation()。
怎么能避免imageWithData缓存呢?
1. 手动调用 CGImageSourceCreateWithData() 来创建图片,并把 ShouldCache 和 ShouldCacheImmediately 关掉。这么做会导致每次图片显示到屏幕时,解码方法都会被调用,造成很大的 CPU 占用。
2. 把图片用 CGContextDrawImage() 绘制到画布上,然后把画布的数据取出来当作图片。这也是常见的网络图片库的做法。
如何判断一个文件的图片类型?
通过读取文件或数据的头几个字节然后和对应图片格式标准进行比对
6> 图片编解码问题.
第一种是 baseline,即逐行扫描。默认情况下,JPEG、PNG、GIF 都是这种保存方式。
第二种是 interlaced,即隔行扫描。PNG 和 GIF 在保存时可以选择这种格式。
第三种是 progressive,即渐进式。JPEG 在保存时可以选择这种方式。
在下载图片时,首先用 CGImageSourceCreateIncremental(NULL) 创建一个空的图片源,随后在获得新数据时调用
CGImageSourceUpdateData(data, false) 来更新图片源,最后在用 CGImageSourceCreateImageAtIndex() 创建图片来显示。
7> 过期文件的处理.