移动端对大批量图片加载的优化方法(一)

2024-01-09 11:41:02

移动端对大批量图片加载的优化方法(一)iOS

本篇主要从iOS开发中可以使用到的对大批量图片加载的优化方法进行整理。优化

1.异步加载

将图片加载任务放在后台线程中进行,避免阻塞主线程,这样可以保证应用的响应性和流畅性;

a.使用Operation和OperationQueue

NSOperation和NSOperationQueue是Apple提供的用于多线程处理的框架,可以为每个图片请求创建一个NSBlockOperation,然后添加到NSOperationQueue中;
优点:高度可定制,可以实现复杂的异步操作;
缺点:相对复杂,需要更多的代码和配置。

class AsyncImageLoader: NSObject {  
      
    static let shared = AsyncImageLoader()  
      
    private let operationQueue = OperationQueue()  
      
    func loadImages(withURLs imageURLs: [URL], completion: @escaping ([UIImage]) -> Void) {  
        if imageURLs.isEmpty {  
            completion([])  
            return  
        }  
          
        let operation = BlockOperation {  
            let loadedImages = self.loadImagesFromURLs(imageURLs)  
            completion(loadedImages)  
        }  
        operation.addDependency(BlockOperation { completion() })  
        operationQueue.addOperation(operation)  
    }  
      
    private func loadImagesFromURLs(_ imageURLs: [URL]) -> [UIImage] {  
        var loadedImages = [UIImage]()  
        var failedImages = [URL]()  
        for url in imageURLs {  
            let loadImageOperation = BlockOperation {  
                if let image = self.loadImage(from: url) {  
                    loadedImages.append(image)  
                } else {  
                    failedImages.append(url)  
                }  
            }  
            loadImageOperation.completionBlock = {  
                if failedImages.count > 0 {  
                    print("Failed to load images: \(failedImages)")  
                }  
            }  
            operationQueue.addOperation(loadImageOperation)  
        }  
        return loadedImages  
    }  
      
    private func loadImage(from url: URL) -> UIImage? {  
        // 在这里实现图片加载的逻辑,可以使用 URLSession 来异步加载数据,然后使用 UIImage 来解码图片。  
        return nil // 返回加载的图片,如果加载失败则返回 nil。  
    }  
}
b.使用GCD(Grand Central Dispatch)

GCD是Apple提供的一种在iOS和OS X上进行并发编程的解决方案,可以使用dispatch_async在后台线程加载图片;
优点:简单易用,性能良好;
缺点:对于更复杂的并发需求,可能需要更多的代码和配置。

class ImageLoader {  
      
    func loadImages(withURLs imageURLs: [URL], completion: @escaping ([UIImage]) -> Void) {  
        if imageURLs.isEmpty {  
            completion([])  
            return  
        }  
          
        let dispatchGroup = DispatchGroup()  
        var loadedImages = [UIImage]()  
        var failedImages = [URL]()  
          
        for url in imageURLs {  
            dispatchGroup.enter()  
              
            URLSession.shared.dataTask(with: url) { (data, response, error) in  
                if let error = error {  
                    failedImages.append(url)  
                    dispatchGroup.leave()  
                    return  
                }  
                  
                if let httpResponse = response as? HTTPURLResponse,  
                  httpResponse.statusCode != 200,  
                  let data = data {  
                    failedImages.append(url)  
                    dispatchGroup.leave()  
                    return  
                } else if let image = UIImage(data: data) {  
                    loadedImages.append(image)  
                } else {  
                    failedImages.append(url)  
                }  
                  
                dispatchGroup.leave()  
            }.resume()  
        }  
          
        dispatchGroup.notify(queue: .main) {  
            completion(loadedImages)  
            if failedImages.count > 0 {  
                print("Failed to load images: \(failedImages)")  
            }  
        }  
    }  
}
c.使用SDWebImage或Kingfisher等第三方库

这些库处理了异步加载和缓存等,可以非常简单的使用;
优点:简单易用,通常包含缓存机制和优化;
缺点:依赖于第三方库,可能不适合所有项目。

let url = URL(string: "https://example.com/image.jpg") // 替换为您的图片 URL  
let resource = ImageResource(downloadURL: url!)  
  
KingfisherManager.shared.retrieveImage(with: resource) { (image, error, cacheType, imageURL) in  
    if let image = image {  
        // 在这里处理加载完成的图片  
        imageView.image = image  
    } else if let error = error {  
        // 处理加载错误  
        print("Error: \(error.localizedDescription)")  
    }  
}

KingfisherManager.shared.cache.storeImage(image, forKey: "myKey", original: resource)

imageView.kf.setImage(with: resource) // 使用 Kingfisher 设置图片

KingfisherManager.shared.cache.clearMemoryCache() // 清空内存缓存  
KingfisherManager.shared.cache.clearDiskCache() // 清空磁盘缓存
d.使用Swift的并发特性

在Swift5.5引入,可以使用async/await来异步执行代码;
优点:语法简单,易于理解;
缺点:需要Swift5.5+版本。

func loadImage(from url: URL) async -> UIImage? {  
    let data: Data? = try? await Data.init(contentsOf: url)  
    if let data = data {  
        return UIImage(data: data)  
    }  
    return nil  
}  
  
// 使用示例  
let url = URL(string: "https://example.com/image.jpg") // 替换为实际的图片URL  
let image = await loadImage(from: url!)  
imageView.image = image

2.懒加载(Lazy Loading)

常用的优化技术,它只在需要显示图片时才开始加载,而不是一次性加载所有图片,可以减少内存占用和加载时间;

a.添加懒加载属性

为需要懒加载的图片设置一个属性;
使用didMoveToSuperview或didMoveToWindow方法来检查图片是否可见;
只有当图片显示时才开始加载图片。

b.使用第三方库

SDWebImage或Kingfisher已经内置了懒加载功能;
使用这些库可以简化懒加载实现过程。

imageView.kf.lazy(with: resource).placeholder(with: nil).into(imageView)

c.自定义ImageView

创建自定义的ImageVIew的子类,在layoutSubviews方法中检查图片是否可见,只有在图片即将显示时调用setImage方法。

d.ReactiveCocoa或RxSwift

使用响应式编程框架可以声明式方法处理懒加载,使用观察者模式来观察UI元素的状态,并在需要时触发图片加载。

e.避免一次性加载所有图片

即使使用了懒加载,一次性加载大量图片仍然可能导致内存问题,使用合适的缓存策略,并及时释放不再需要的资源。

f.预加载

在用户滚动视图时,预加载即将显示的图片,可以通过监听滚动事件并提前开始加载图片。

g.监控和调试

使用Xcode的Instruments工具来监控内存使用和性能瓶颈,调试和优化懒加载实现,确保它在各种情况下都能正常工作。

3.缓存机制

通过缓存,可以存储已加载的图片,以便需要时快速检索,而不是重新从网络或其他来源加载;

a.标准缓存策略

iOS提供了NSURLCache类,可以用于缓存HTTP请求和响应,可以通过配置NSURLCache的内存和磁盘容量以适应应用需求。

private func setupURLCache() {  
        // 创建一个自定义的URL缓存对象  
        let cache = URLCache(memoryCapacity: 0, diskCapacity: 0, diskPath: "myCustomCachePath")  
        URLCache.shared = cache  
    }  
  
private func loadImages() {  
        // 模拟加载大量图片  
        for i in 1...100 {  
            let url = URL(string: "https://example.com/image_\(i).jpg")!  
            let request = URLRequest(url: url)  
            URLSession.shared.dataTask(with: request) { (data, response, error) in  
                if let error = error {  
                    print("Error loading image: \(error.localizedDescription)")  
                    return  
                }  
                if let response = response as? HTTPURLResponse, let data = data {  
                    // 将响应数据缓存到磁盘中  
                    URLCache.shared.storeCachedResponse(response, for: request)  
                }  
            }.resume()  
        }  
    }  
b.自定义缓存策略

对于更复杂的用例,可以实现自己的缓存策略;
考虑使用键值存储(如UserDefaults或CoreData)或第三方缓存库(Haneke或Flare)。

// 配置Flare  
    Flare.shared.config = .init(diskCacheDirectory: "flare_cache", diskCacheSizeLimit: 100 * 1024 * 1024) // 100MB  
    
    // 使用Flare加载图片并缓存  
Flare.shared.load(imageURL: imageURL) { (result) in  
    switch result {  
    case .success(let image):  
        imageView.image = image  
    case .failure(let error):  
        print("加载图片失败: \(error.localizedDescription)")  
    }  
}

Flare.shared.clearDiskCache() // 清理磁盘缓存

c.压缩和优化

将图片存储到缓存之前,对其进行适当压缩和优化,可以减少缓存的大小并提高加载速度。

func compressImage(_ image: UIImage, quality: CGFloat = 0.8) -> Data? {  
    var compressedData: Data?  
    let imageRef: CGImage = image.cgImage?.cropping(to: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))  
    if let imageRep = CGImageRepresentation(imageRef) {  
        compressedData = imageRep.jpegData(options: [.compressionFactor: quality])  
    }  
    return compressedData  
}  
d.定期清理缓存

定期清理过是或不常用的缓存数据以释放空间并保持缓存的有效性;
可以使用定时任务或后台任务来执行清理操作。

f.监听网络和响应时间

监听设备的网络状态变化,并根据网络条件动态调整图片加载策略;
监测服务器响应时间,以便在必要时进行重试或回退到缓存数据。

g.离线优先

在用户离线时提供预先加载和缓存的图片,使用后台任务和后台刷新来预先获取数据。

4.优化图片质量

加载图片时,可以根据需要调整图片的质量(可以通过降低分辨率或压缩图片来减少加载时间和内存占用)。

// 创建一个UIImage对象,并使用缩略图模式加载图片  
let image = UIImage(named: "example.jpg")?.generateThumbnail(width: 100)  
  
// 将缩略图设置到UIImageView中  
let imageView = UIImageView(image: image)

5.使用适当的图片格式

选择适当的图片格式对于优化图片加载性能和节省存储空间很重要;

a.JPEG(Joint Photographic Experts Group)

特点:广泛使用的格式,支持有损压缩;
适用场景:照片和其他需要高质量细节的图像。

b.PNG(Portable Network Graphics)

特点:无损压缩,支持透明度和alpha通道;
适用场景:图标、按钮和其他需要透明背景的图像。

c.GIF(Graphics Interchange Format)

特点:支持动画和透明度;
适用场景:动画和循环显示的简单图形。

d.TIFF(Tagged Image File Format)

特点:支持多种压缩算法和颜色模式;
适用场景:需要高质量和多页的文档或图像。

e.WebP(Web Picture)

特点:由Google开发的现代格式,支持有损和无损压缩;
适用场景:需要在网页上快速加载的图片,以及需要节省带宽和存储的应用。

f.HEIF(High Efficiency Image File Format)

特点:高效压缩,支持高分辨率和透明度;
适用场景:iPhone和iPad上的照片,以及其他需要搞笑存储和传输的图像。

g.APNG(Animated Portable Network Graphics)

特点:类似于GIF的动画格式,但支持更高的图像质量和更小的文件大小;
适用场景:需要展示动画效果的网页和其他应用程序。

h.SVG(Scalable Vector Graphics)

特点:基于矢量的格式,可缩放而不失清晰度;
适用场景:需要高分辨率和适应不同屏幕尺寸的图形设计。

i.PDF(Portable Document Format)

支持矢量图形和高质量打印输出,适用于电子文档、表单和需要精确布局的图像。

j.RAW

高质量的原始图像格式,通常用于专业摄影和需要无损质量的场合;
适用场景:需要最大程度保留原始图像数据的情境。

k.Apple ProRAW

Apple专有的原始图像格式,结合了RAW和JPEFG的优势,适用于iPhone和iPad上的专业摄影;
适用场景:需要高质量、灵活且易于分享的摄影作品。

6.组织图片资源

将图片资源合理组织起来,以方便管理和查找,提高加载效率;

a.适当的目录结构

将图片资源按功能或用途分类,放在不同的文件夹中。

b.合适的文件命名约定

为图片文件使用描述性的、易于理解的名称,避免使用随机或过于复杂的文件名。

c.利用资产库(Asset Catalog)
d.使用适当的请求头和响应头

根据服务器的设置,通过设置请求头和响应头来优化图像的加载和缓存(Cache-Control、ETag)。

文章来源:https://blog.csdn.net/weixin_42979360/article/details/135471504
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。