批量获取影视内容及海报时的内存性能优化求助
我正在开发一款影视类应用,流程是从URL获取电影/剧集详情并解码为ContentModel,随后尝试下载模型中posterUrl对应的海报图片。但添加海报下载逻辑后,调用约50次解析函数时,内存占用从稳定的25MB飙升至800MB,这一情况不可持续,急需优化内存性能。
相关代码
ContentModel定义
struct ContentModel: Decodable { let url: URL let title: String let type: String let posterUrl: URL let description: String let aggregateRating: AggregateRating? let contentRating: String? let genre: [String] let trailer: Trailer? let actor: [Individual] let director: [Individual] // 推测新增的海报存储属性 var poster: UIImage? }
解析与图片下载函数
func parseContentJSON(_ contentID: String) async throws -> ContentModel { // 隐藏代码:设置URL、发起请求、获取响应与数据 do { var content = try jsonDecoder.decode(Short.self, from: data).short content.poster = await downloadImage(from: content.posterUrl) // 导致内存飙升的代码行 return content } catch { throw ParseError.invalidData } }
并发加载上下文
let contentsID: [String] = [...] // 约50个ID func loadContentConcurrent(start startIndex: Int, end endIndex: Int) async { var all: [ContentModel] = [] try? await withThrowingTaskGroup(of: ContentModel?.self) { group in for contentID in contentsID[startIndex..<endIndex] { group.addTask { return try? await self.parseContentJSON(contentID) } } for try await contentFound in group { if let safeContent = contentFound { all.append(safeContent) } } return } DispatchQueue.main.async { [weak self, all] in self?.contents += all self?.contentLoadedCount += all.count } }
补充说明:上述属性与方法均位于ViewModel内,初始化时调用一次该函数,后续在关联View中重复调用;采用分页策略(每次调用处理5个ID,而非一次性处理全部50个ID)。
优化方案
避免在ContentModel中直接存储UIImage实例
直接在模型中保存UIImage是内存暴涨的核心原因——每张高清海报的内存占用可达十几MB,50张叠加就会轻松突破800MB。建议只保留posterUrl,在View层需要显示海报时再按需加载;如果必须提前处理,可改用Data存储(但优先缓存到磁盘而非内存),通过缓存机制管理内存占用。限制并发任务数量
当前TaskGroup会同时启动所有分页任务,过多并发的图片下载会导致内存中同时存在大量未释放的图片数据。可以通过limitingConcurrentTaskCount参数限制并发数(比如设置为3-5),避免系统同时处理过多下载任务:try? await withThrowingTaskGroup(of: ContentModel?.self, limitingConcurrentTaskCount: 3) { group in // 原有任务添加逻辑 }按需解码图片,缩小内存占用
检查downloadImage(from:)的实现,避免加载原始尺寸的图片。根据实际显示需求(比如列表缩略图只需要200px宽),解码时生成对应尺寸的缩略图,大幅降低单张图片的内存占用:func downloadImage(from url: URL) async throws -> UIImage? { let (data, _) = try await URLSession.shared.data(from: url) guard let imageSource = CGImageSourceCreateWithData(data as CFData, nil) else { return nil } let options: [CFString: Any] = [ kCGImageSourceCreateThumbnailFromImageAlways: true, kCGImageSourceThumbnailMaxPixelSize: 200 // 根据实际显示需求调整 ] guard let cgImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary) else { return nil } return UIImage(cgImage: cgImage) }使用NSCache管理图片内存缓存
如果需要提前缓存海报,不要用普通字典存储(会一直持有内存),改用NSCache——它会在系统内存不足时自动释放缓存内容,避免内存溢出。示例:let imageCache = NSCache<NSString, UIImage>() func getCachedImage(for url: URL) -> UIImage? { return imageCache.object(forKey: url.absoluteString as NSString) } func cacheImage(_ image: UIImage, for url: URL) { imageCache.setObject(image, forKey: url.absoluteString as NSString) }及时释放无用资源
确保downloadImage函数中没有强引用不必要的对象,比如闭包中使用weak self避免循环引用;同时,在View不再需要显示海报时,及时将对应模型的poster属性置为nil,让系统可以回收内存。
内容的提问来源于stack exchange,提问作者Yousif Ismat

