侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

设计高性能图片缓存组件并处理大图内存优化

2025-12-12 / 0 评论 / 3 阅读

题目

设计高性能图片缓存组件并处理大图内存优化

信息

  • 类型:问答
  • 难度:⭐⭐⭐

考点

内存管理,多线程同步,缓存策略,图像处理,性能优化

快速回答

设计高性能图片缓存需实现:

  • 三级缓存架构:内存缓存(NSCache)+ 磁盘缓存(FileManager)+ 网络下载
  • LRU淘汰策略:通过双向链表实现最近最少使用淘汰
  • 大图处理:使用ImageIO进行子采样(downsampling)
  • 线程安全:GCD串行队列配合barrier保证读写安全
  • 解码优化:后台线程解码并缓存解码后图像
## 解析

1. 核心架构设计

三级缓存流程:

  1. 检查内存缓存(NSCache)
  2. 未命中则检查磁盘缓存(文件系统)
  3. 最后发起网络请求并缓存结果
class ImageCache {
    private let memoryCache = NSCache<NSString, UIImage>()
    private let diskCacheDir: URL
    private let ioQueue = DispatchQueue(label: "com.cache.io", qos: .utility)

    func image(for url: URL) -> UIImage? {
        // 1. 检查内存缓存
        if let image = memoryCache.object(forKey: url.absoluteString as NSString) {
            return image
        }

        // 2. 检查磁盘缓存
        var diskImage: UIImage?
        ioQueue.sync {
            let filePath = diskCachePath(for: url)
            diskImage = UIImage(contentsOfFile: filePath.path)
        }

        // 3. 网络请求(伪代码)
        if diskImage == nil {
            downloadImage(url) { image in
                self?.storeImage(image, for: url)
            }
        } else {
            memoryCache.setObject(diskImage!, forKey: url.absoluteString as NSString)
        }
        return diskImage
    }
}

2. 内存优化关键技术

大图子采样(Downsampling):

func downsample(imageAt sourceURL: URL, to maxSize: CGSize) -> UIImage? {
    let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
    guard let imageSource = CGImageSourceCreateWithURL(sourceURL as CFURL, imageSourceOptions) else { return nil }

    let options: [CFString: Any] = [
        kCGImageSourceThumbnailMaxPixelSize: max(maxSize.width, maxSize.height),
        kCGImageSourceCreateThumbnailFromImageAlways: true,
        kCGImageSourceCreateThumbnailWithTransform: true
    ]

    guard let downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary) else { return nil }
    return UIImage(cgImage: downsampledImage)
}

优势:避免加载全尺寸Bitmap到内存,内存占用降低80%+

3. 缓存策略实现

LRU双向链表实现:

class LRUCache<Key: Hashable, Value> {
    private class Node {
        var key: Key
        var value: Value
        var prev: Node?
        var next: Node?
    }

    private var head: Node?, tail: Node?
    private var cacheDict = [Key: Node]()
    private let lock = NSLock()

    func setValue(_ value: Value, for key: Key) {
        lock.lock()
        defer { lock.unlock() }

        if let node = cacheDict[key] {
            node.value = value
            moveToHead(node)
        } else {
            let newNode = Node(key: key, value: value)
            addNode(newNode)
            cacheDict[key] = newNode
        }
    }

    // 淘汰逻辑(伪代码)
    private func removeTail() {
        guard let tailNode = tail else { return }
        cacheDict.removeValue(forKey: tailNode.key)
        // 移除尾节点并更新链表
    }
}

4. 线程安全方案

  • 读写锁模式:使用GCD barrier保证写操作独占
  • 内存缓存:NSCache自带线程安全
  • 磁盘操作:专用串行队列管理
private let concurrentQueue = DispatchQueue(
    label: "com.cache.concurrent", 
    attributes: .concurrent
)

func safeSetImage(_ image: UIImage, for key: String) {
    concurrentQueue.async(flags: .barrier) {
        self.memoryCache.setObject(image, forKey: key as NSString)
        self.saveToDisk(image, key: key) // 同步保存
    }
}

5. 常见错误与优化

错误后果解决方案
主线程解码大图界面卡顿后台线程解码+子采样
未限制缓存大小内存溢出崩溃设置costLimit并监听内存警告
重复下载相同URL资源浪费使用NSOperationQueue管理下载任务
磁盘缓存未压缩空间浪费存储JPEG格式(有损)或HEIC(iOS11+)

6. 扩展知识

  • 内存警告处理:监听UIApplication.didReceiveMemoryWarningNotification清空NSCache
  • 渐进式加载:使用CGImageSourceCreateIncremental实现模糊→清晰加载
  • WebP支持:集成libwebp解码器获得更高压缩率
  • 缓存验证:HTTP ETag/Last-Modified头避免重复下载