精选文章

10. 图片优化

2018-10-10 · 图片

10. 图片优化

图片是性能瓶颈的高发区,核心问题是:解码成本、内存占用、重复加载。下面按链路拆开处理。

一、缩略图与下采样

import ImageIO

func downsample(data: Data, to size: CGSize, scale: CGFloat) -> UIImage? {
    let options = [kCGImageSourceShouldCache: false] as CFDictionary
    guard let source = CGImageSourceCreateWithData(data as CFData, options) else { return nil }

    let maxDimension = max(size.width, size.height) * scale
    let downsampleOptions = [
        kCGImageSourceCreateThumbnailFromImageAlways: true,
        kCGImageSourceShouldCacheImmediately: true,
        kCGImageSourceCreateThumbnailWithTransform: true,
        kCGImageSourceThumbnailMaxPixelSize: maxDimension
    ] as CFDictionary

    guard let image = CGImageSourceCreateThumbnailAtIndex(source, 0, downsampleOptions) else { return nil }
    return UIImage(cgImage: image)
}

二、解码放到后台线程

DispatchQueue.global().async {
    let image = UIImage(data: data)
    DispatchQueue.main.async {
        imageView.image = image
    }
}

三、内存缓存

final class ImageCache {
    static let shared = ImageCache()
    private let cache = NSCache<NSString, UIImage>()

    func get(_ key: String) -> UIImage? { cache.object(forKey: key as NSString) }
    func set(_ image: UIImage, key: String) { cache.setObject(image, forKey: key as NSString) }
}

四、磁盘缓存

final class DiskCache {
    private let folder: URL

    init(folderName: String) {
        let base = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
        folder = base.appendingPathComponent(folderName)
        try? FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true)
    }

    func save(_ data: Data, key: String) throws {
        try data.write(to: folder.appendingPathComponent(key), options: .atomic)
    }

    func load(key: String) -> Data? {
        try? Data(contentsOf: folder.appendingPathComponent(key))
    }
}

五、列表预加载

extension FeedVC: UITableViewDataSourcePrefetching {
    func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
        let urls = indexPaths.compactMap { items[$0.row].coverURL }
        urls.forEach { loadImage(url: $0) }
    }
}

六、避免离屏渲染

  • 圆角 + 阴影容易触发离屏
  • 尽量设置 shadowPath
  • 大量图片尽量用圆角图片资源或预处理
imageView.layer.cornerRadius = 8
imageView.layer.masksToBounds = true

七、URLCache 与网络缓存

let cache = URLCache(memoryCapacity: 20 * 1024 * 1024,
                     diskCapacity: 100 * 1024 * 1024,
                     diskPath: "image-cache")
let config = URLSessionConfiguration.default
config.urlCache = cache
config.requestCachePolicy = .returnCacheDataElseLoad

图片优化的关键是“先降成本,再复用”。减少解码和重复加载,滚动体验会明显提升。

JJ

作者简介

专注于内容创作、产品策略与设计实践。欢迎交流合作。

上一篇 下一篇