SwiftUI中Image相关内存泄漏警告问题求助
内存泄漏分析与解决方法
问题原因
你的代码中内存泄漏的核心诱因是Model类里photosPickerItem的didSet中启动的异步Task——该Task会隐式捕获Model实例的引用。当用户点击Clear按钮清空imageData,或者多次切换选择照片时,未完成的旧Task仍会持有Model实例,导致其无法被系统回收,进而触发内存泄漏警告。
另外原代码存在两处冗余/风险点:
- 逐个判断图片类型属于冗余操作,
UTType.image是所有图片格式的父类型,可覆盖你列出的所有格式; - ContentView中使用强制解包
!,存在运行时崩溃风险。
修复后的代码
Model类优化(处理Task生命周期)
@MainActor class Model: ObservableObject{ @Published var photosPickerItem: PhotosPickerItem? { didSet{ // 取消未完成的旧任务,避免持有无效引用 loadTask?.cancel() guard let photosPickerItem = photosPickerItem else { return } // 保存新任务引用,便于后续管理 loadTask = Task{ do { try await loadTransferrable(from: photosPickerItem) } catch is CancellationError { // 任务被取消属于预期情况,无需额外处理 } catch { print("图片加载失败:\(error.localizedDescription)") } } } } @Published var imageData: Data? // 持有加载任务的引用,用于取消操作 private var loadTask: Task<Void, Error>? func loadTransferrable(from item: PhotosPickerItem) async throws{ // 简化类型检查,UTType.image覆盖所有支持的图片格式 guard item.supportedContentTypes.contains(.image) else { return } if let data = try await item.loadTransferable(type: Data.self){ imageData = data } } func clearImage(){ imageData = nil // 可选:若需要立即终止未完成的加载任务,可取消 // loadTask?.cancel() } // 实例销毁时自动取消任务,彻底释放资源 deinit { loadTask?.cancel() } }
ContentView优化(移除强制解包)
struct ContentView: View { @State var isShowingPicker = false @StateObject var model = Model() var body: some View { ZStack { // 安全解包,避免强制解包导致的崩溃 if let imageData = model.imageData, let uiImage = UIImage(data: imageData) { Image(uiImage: uiImage) .resizable() .scaledToFit() } HStack{ Button("Picker Button") { isShowingPicker.toggle() } Button("Clear Button") { model.clearImage() } } } .photosPicker(isPresented: $isShowingPicker, selection: $model.photosPickerItem) } }
修复说明
- Task生命周期管理:通过
loadTask引用跟踪异步任务,在photosPickerItem更新时取消旧任务,在Model实例销毁时自动取消剩余任务,打破Task对Model的持有引用,确保实例能被正常回收。 - 简化类型检查:利用
UTType.image的父类型特性,减少冗余代码,同时覆盖所有图片格式。 - 安全解包:替换强制解包为可选绑定,避免因数据异常导致的运行时崩溃。
内容的提问来源于stack exchange,提问作者nick
相关产品推荐
相关产品推荐

