You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

批量获取影视内容及海报时的内存性能优化求助

内存优化问题:批量加载影视内容时图片下载导致内存飙升

我正在开发一款影视类应用,流程是从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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 01:04:57