UIImage内存未释放异常:点击两次清理才释放的原因探究
SwiftUI 图片清理后内存延迟释放问题
加载图片数据后点击「clean data」按钮清空数据列表,内存并未立即释放,必须连续点击两次按钮,内存才会被回收。分别使用UIImage(contentsOf:)和UIImage(data:)方法加载图片,现象完全一致。
测试代码
import SwiftUI import Foundation struct MainView: View { @State var data: [Data] = [] @State var urls: [URL] = [] var body: some View { VStack { Button("clean data") { data.removeAll() urls.removeAll() } Button("load data") { loadData() } HStack { ForEach(urls, id: \.self) { url in Image(uiImage: UIImage(contentsOfFile: url.path)!) .resizable() .scaledToFit() } } } } func loadData() { for _ in 0...5 { urls.append(Bundle.main.url(forResource: "output\(Int.random(in: 0...2))", withExtension: "png")!) } } }
Instrument内存监测截图

问题原因
- SwiftUI视图更新延迟:第一次清空
urls后,SwiftUI的视图更新流程尚未完成旧Image视图的销毁,旧视图持有的UIImage实例仍被引用,无法被回收。 - UIImage系统缓存:通过文件路径或Data加载的
UIImage会被系统自动缓存,即使数据源被清空,缓存的图片对象可能仍未被标记为可回收,第二次清理操作触发了系统缓存的回收逻辑。
解决方案
方案1:强制视图刷新+触发自动释放
在清空数据后,手动触发视图刷新和自动释放池,加速内存回收:
Button("clean data") { data.removeAll() urls.removeAll() // 强制通知视图更新 objectWillChange.send() // 触发自动释放池回收对象 autoreleasepool { } }
方案2:用ObservableObject管理图片生命周期
将图片对象托管到可观察对象中,明确控制其生命周期,避免SwiftUI视图隐式持有:
class ImageManager: ObservableObject { @Published var images: [UIImage] = [] func loadImages() { images.removeAll() for _ in 0...5 { guard let url = Bundle.main.url(forResource: "output\(Int.random(in: 0...2))", withExtension: "png"), let image = UIImage(contentsOfFile: url.path) else { continue } images.append(image) } } func cleanImages() { images.removeAll() autoreleasepool { } } } struct MainView: View { @StateObject var imageManager = ImageManager() var body: some View { VStack { Button("clean data") { imageManager.cleanImages() } Button("load data") { imageManager.loadImages() } HStack { ForEach(imageManager.images.indices, id: \.self) { index in Image(uiImage: imageManager.images[index]) .resizable() .scaledToFit() } } } } }
方案3:绕过UIImage系统缓存
如果不需要系统缓存,可以通过CGImage创建UIImage,避免被系统自动缓存:
// 加载图片时改用此方式 guard let url = Bundle.main.url(forResource: "output\(Int.random(in: 0...2))", withExtension: "png"), let data = try? Data(contentsOf: url), let cgImage = UIImage(data: data)?.cgImage else { continue } let image = UIImage(cgImage: cgImage)
内容的提问来源于stack exchange,提问作者Perry Wang
相关产品推荐
相关产品推荐

