在看这篇文章前,建议先看下面两篇文章:
最近一直在写公司里的开源项目 JChat ,这里记录下其中优化过程。
本地图片加载的方法
方法名 | 是否缓存 |
---|---|
1. UIImage(named: <#T##String#>) | 会一直缓存在内存中 |
2. UIImage(contentsOfFile: <#T##String#>) | 每次都是重新加载 |
通过方法1来加载图片时,图片会一直缓存在内存中,一般情况不推荐使用这种方式,一个是缓存不可控;第二个图片资源得直接加载到项目中,不方便管理,而且每次添加新图片时工程文件都会变动。
通过方法2来加载图片时,每次都会从硬盘里读图片,这个操作是比较耗时的,文章开头处的两篇文章都是以这个方法来加载图片的。
JChat 优化处理
在 JChat Swift 中,图片资源使用的是 再谈 Swift 换肤功能 一文中的管理方式,就是通过 bundle 来管理图片,这里就不详细说。 上面也说过了,通过 UIImage(contentsOfFile: <#T##String#>) 来访问图片时,每次都需要从硬盘里加载,这样的话,当需要频繁加载图片的时候,通过这种方式去加载图片,就有可能成为性能的瓶颈。
那么我们该如何去优化呢,它的缺点就是没有缓存,这样的话就手动去给它添加缓存,这里直接通过 NSCache 来做就可以:
// 缓存 image 到内存中,提高重复访问的速度
private let memoryCache = NSCache<NSString, UIImage>()
在访问图片资源的时候,如果缓存有该图片,就直接从缓存中返回,如果缓存中不存在,就从硬盘里读,并把读出来的图片缓存起来:
public func loadImage(_ imageName: String, _ style: ThemeStyle) -> UIImage? {
....
if let image = memoryCache.object(forKey: name as NSString) {
return image
}
....
if let imagePath = imagePath {
image = UIImage(contentsOfFile: imagePath)
}
if let image = image {
memoryCache.setObject(image, forKey: name as NSString)
}
return image
}
这样就搞定了么,显然并没有,虽然这里加了缓存,但还是存在上述方法1中的缓存不可控制问题,当应用内存过高的时候,应用就有可能闪退,所以还得监听系统是否有内存警告:
// 收到内存警告时,移除所有缓存
NotificationCenter.default.addObserver(
self, selector: #selector(clearMemoryCache), name: .UIApplicationDidReceiveMemoryWarning, object: nil)
如果收到内存警告的时候就把缓存清空:
@objc private func clearMemoryCache() {
memoryCache.removeAllObjects()
}
这样就可以解决上述方法1和方法2的缺陷了,但需要注意的是,这里的缓存并没有设置数量的上限,因为资源图片本身就比较小,所以占用的空间并不会很大,故没有设置缓存数量的限制。如果需要设置数量限制的话,需要考虑的事情就比较多了,比如说移除时的优先级等,建议是自己实现 cache 的数据结构,这样会更加灵活了,这里就不一一详谈了,可以去看下 YYCache。
网友评论