跳转到正文
YGJoon 技术博客

iOS 图片缓存与对象存储:actor 并发去重和尺寸变体

发布于:
·683 字 · 约 2 分钟·作者:YGJoon
使用 actor 合并相同图片请求、内存缓存已解码图像,并按显示场景生成对象存储裁剪参数。
iOS 图片缓存与对象存储:actor 并发去重和尺寸变体文章封面
目录

示例代码均为匿名化改写,用于说明模式;生产实现还应限制下载大小并根据显示尺寸下采样。

相同图片避免重复下载

一个页面上的多个卡片可能同时引用同一 URL。图片管线可以用 actor 按 URL 合并在途任务,再把成功解码的图像放入 NSCache。内存缓存适合加速滚动和会话内复用,不应被描述为离线持久存储。

actor ImageRequestPool {
    private struct Entry {
        let id: UUID
        let task: Task<Data, Error>
    }

    private var inFlight: [URL: Entry] = [:]

    func data(for url: URL, session: URLSession = .shared) async throws -> Data {
        let entry: Entry
        if let existing = inFlight[url] {
            entry = existing
        } else {
            let id = UUID()
            let task = Task {
                let (data, response) = try await session.data(from: url)
                guard (response as? HTTPURLResponse)?.statusCode == 200 else {
                    throw URLError(.badServerResponse)
                }
                return data
            }
            entry = Entry(id: id, task: task)
            inFlight[url] = entry
        }

        do {
            let result = try await entry.task.value
            if inFlight[url]?.id == entry.id { inFlight[url] = nil }
            return result
        } catch {
            if inFlight[url]?.id == entry.id { inFlight[url] = nil }
            throw error
        }
    }
}

远程图片视图可以把加载结果转成占位、成功和失败状态,并提供无障碍描述。占位动画应遵守减少动态效果设置;失败图也要有明确、稳定的呈现。

共享下载任务不应由单个离屏 cell 的取消动作直接取消;可以用订阅计数管理取消,也可以让共享下载完成后写入缓存,再由各个视图独立决定是否继续等待。

按显示场景请求合适尺寸

列表缩略图、头像、横幅和全屏预览对分辨率及裁剪方式有不同要求。统一的 URL 适配层可以按场景生成处理参数,减少客户端下载远超显示尺寸的原图。

若源地址含有签名查询参数,应避免随意改写 URL,否则服务端签名可能失效。对不受支持的主机则保留原地址。

仍需关注单张大图

内存缓存的成本上限是在图像解码之后统计,不能阻止单张大图在解码瞬间占用大量内存。若图片尺寸不受控,可以在解码前限制像素尺寸并使用下采样,同时限制下载体积、校验响应类型并检查页面取消后的任务生命周期。

NSCache.countLimit 限制的是条目数,不是实际内存。应按解码成本设置预算,并在解码前按显示尺寸下采样,避免少量大图占满内存。


分享这篇文章:


相关文章

上一篇
SwiftUI 详情页并发加载:请求竞态与导航状态保留
下一篇
Swift actor 网络客户端设计:共享上下文隔离与请求取消