LRU在UIImageView的extension中简单应用
创始人
2024-05-28 16:14:34
0

有时候简单写一个demo需要对UIImageView来一个根据url设置image的操作,单独pod install一下或者找个spm感觉太麻烦,隧根据URLSession写一个简单的extension,如下:

直接extension

extension UIImageView {func sg_setImage(_ urlString: String, placeholderName placeholder: String? = nil) {if let placeholder = placeholder, let placeholderImage = UIImage(named: placeholder) {self.image = placeholderImage}DispatchQueue.global().async {guard let url = URL(string: urlString) else { return }URLSession.shared.dataTask(with: url) { data, response, error  inif let data = data, let image = UIImage(data: data) {DispatchQueue.main.async {self.image = image}} else {print(error ?? "")}}.resume()}}}

使用时,调用UIImageView实例对象的sg_setImage()方法即可,还额外实现了placeholder功能。

但是每次调用都会重新请求一下,如果一个CollectionView里面的cell有不少内容是重复的,那岂不是做了重复的工作?这是不可接受的,因此,自然而言地想到了使用hash字典来充当缓存。

因为加载重复image的首要条件就是url相同,那么就可以把url当作hash字段的key,这样,每次调用sg_setImage()方法时候根据url作为key先判断缓存里面是否存在image对象,如果有,直接显示即可,而没有的话,先走placeholder的路径,然后异步执行Session的任务,下载完后将image对象放入缓存里面,再设置image即可。内容如下:

extension + 字典缓存

extension UIImageView {private static var imageCaches: [String: Any] = { [: ] }()func sg_setImage(_ urlString: String, placeholderName placeholder: String? = nil) {if UIImageView.imageCaches[urlString] != nil {self.image = UIImageView.imageCaches[urlString] as? UIImagereturn}if let placeholder = placeholder, let placeholderImage = UIImage(named: placeholder) {self.image = placeholderImage}DispatchQueue.global().async {guard let url = URL(string: urlString) else { return }URLSession.shared.dataTask(with: url) { data, response, error  inif let data = data, let image = UIImage(data: data) {DispatchQueue.main.async {UIImageView.imageCaches[urlString] = imageself.image = image}} else {print(error ?? "")}}.resume()}}}

然而随着深入使用发现了另一个问题,每个不同url的请求都会将image放入缓存中,那要不了多久内存就会被撑爆,这同样也是不可接受的。在<<操作系统>>课程里面有个很经典的算法叫做LRU(Latest Recently Used),叫做“最近最久使用”。该算法就是解决页面里面置换问题。操作系统里面的页面置换是有数组来管理的(实际上比数组复杂,可能为双向链表),那这里也可以用LRU去管理,YYCache我记得也是有LRU的应用的,这里不再赘述。

extension + LRU缓存

extension UIImageView {private static var sgImageCaches: Cache = { Cache(limitCount: 100) }()func sg_setImage(_ urlString: String, placeholderName placeholder: String? = nil) {if UIImageView.sgImageCaches.getObject(forKey: urlString) != nil {self.image = UIImageView.sgImageCaches.getObject(forKey: urlString) as? UIImagereturn}if let placeholder = placeholder, let placeholderImage = UIImage(named: placeholder) {self.image = placeholderImage}DispatchQueue.global().async {guard let url = URL(string: urlString) else { return }URLSession.shared.dataTask(with: url) { data, response, error  inif let data = data, let image = UIImage(data: data) {DispatchQueue.main.async {UIImageView.sgImageCaches.setObject(image, forKey: urlString)self.image = image}} else {print(error ?? "")}}.resume()}}}class Node: NSObject {// 上一个节点var pre: Node?// 下一个节点var next: Node?var key: AnyHashablevar value: Anyinit(value: Any, key: AnyHashable) {self.value = valueself.key = keysuper.init()}override var description: String {return "\(key):\(value)"}
}class LinkMap: NSObject {var headNode: Node?var tailNode: Node?var dict = [AnyHashable: Node]()var totalCount: UInt64 = 0/// 插入新元素////// - Parameter node: 元素节点func insert(_ node: Node) {totalCount += 1dict[node.key] = nodeif let head = headNode {node.next = headhead.pre = node// 重新赋值头节点headNode = node} else {headNode = nodetailNode = node}}/// 删除元素////// - Parameter node: 元素节点func removeNode(_ node: Node) {totalCount -= 1dict.removeValue(forKey: node.key)if let _ = node.pre {node.pre?.next = node.next}if let _ = node.next {node.next?.pre = node.pre}if headNode == node {headNode = node.next}if tailNode == node {tailNode = node.pre}}/// 把当前节点移动到首部////// - Parameter node: 元素节点func moveNodeToHead(_ node: Node) {if headNode == node {return}// 删除当前节点if tailNode == node {tailNode = node.pretailNode?.next = nil} else {node.next?.pre = node.prenode.pre?.next = node.next}// 把当前节点移动到头部node.next = headNodenode.pre = nilheadNode?.pre = node// 重新赋值头节点headNode = node}/// 删除尾节点func removeTailNode() -> Node? {totalCount -= 1if let tail = tailNode {let key = tail.keydict.removeValue(forKey: key)}if headNode == tailNode {return nil} else {tailNode = tailNode?.pretailNode?.next = nilreturn tailNode}}/// 删除所有元素节点func removeAllNode() {totalCount = 0headNode = niltailNode = nildict = [AnyHashable: Node]()}
}class Cache: NSObject {var lru = LinkMap()var lock = NSLock()let limitCount: UInt64init(limitCount: UInt64 = 100) {self.limitCount = limitCountsuper.init()}func getObject(forKey key: AnyHashable) -> Any? {lock.lock()var node: Node?node = lru.dict[key]if let node = node {lru.moveNodeToHead(node)}lock.unlock()return node?.value}func setObject(_ value: Any, forKey key: AnyHashable) {lock.lock()if let node = lru.dict[key] {// 存在节点,则把节点移到头部// 如果值不相等,则把新值替换进去node.value = valuelru.moveNodeToHead(node)} else {// 不存在节点,则插入新的节点let node = Node(value: value, key: key)lru.insert(node)}if lru.totalCount > limitCount {// 数量超过限制,则删除尾节点let _ = lru.removeTailNode()}lock.unlock()}func removeObjc(forKey key: AnyHashable) {lock.lock()if let node = lru.dict[key] {lru.removeNode(node)}lock.unlock()}override var description: String {var description = ""var node: Node? = lru.headNodewhile let current = node {description.append("\(current.description) ")node = current.next}return description}
}

这里说明一下:LRU的实现从网上直接copy其他博主的文章过来的,本来想放到链接但没找到

有了LRU的实现,就可以把普通的hash字典换成LRU缓存了,在设置完指定最大容量后,新的url请求过来就会把最久远未使用的image对象移除缓存,达到了动态使用、动态平衡的结果。

相关内容

热门资讯

【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
AsusVivobook无法开... 首先,我们可以尝试重置BIOS(Basic Input/Output System)来解决这个问题。...
ASM贪吃蛇游戏-解决错误的问... 要解决ASM贪吃蛇游戏中的错误问题,你可以按照以下步骤进行:首先,确定错误的具体表现和问题所在。在贪...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...