SwiftUI中使用UIKit展示图片时内存泄漏的解决方法咨询
解决SwiftUI LazyHStack中UIKit图片视图内存无法释放的问题
问题核心原因
- UIViewController生命周期未正确清理:封装的
TestImageDisplay控制器被SwiftUI卸载时,内部的UIImageView和加载的UIImage因强引用未被及时回收,导致内存泄漏。 - 图片加载未做内存优化:直接加载原始大尺寸图片会占用大量内存,即便视图卸载,未清空的图片实例仍会持续占用内存。
- SDWebImage请求未及时取消:滑动时未取消不可见视图的图片加载请求,且未清理已加载的图片缓存引用。
解决方案步骤
1. 重构UIViewController,添加销毁时的清理逻辑
将UIImageView改为实例变量,在视图消失和控制器销毁时主动清空图片、取消加载请求,确保ARC能正常回收内存:
import UIKit import SwiftUI import SDWebImage class TestImageDisplay: UIViewController { var item: TestItem private let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200)) init(item: TestItem) { self.item = item super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() imageView.center = view.center view.addSubview(imageView) loadImage() } private func loadImage() { // 配置SDWebImage选项:优先解码、自动缩小大图,减少内存占用 let options: SDWebImageOptions = [.decodeFirst, .scaleDownLargeImages] imageView.sd_setImage(with: item.imageURL, placeholder: nil, options: options) } // 视图即将消失时,取消请求并清空图片 override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) imageView.sd_cancelCurrentImageLoad() imageView.image = nil } // 验证控制器是否被销毁(可选,用于调试) deinit { print("TestImageDisplay deinit: \(item.id)") imageView.image = nil } }
2. 优化UIViewControllerRepresentable的更新逻辑
确保仅当item变化时才重新加载图片,避免不必要的资源消耗:
struct TestImageDisplayRepresentable: UIViewControllerRepresentable { var item: TestItem func makeUIViewController(context: Context) -> TestImageDisplay { TestImageDisplay(item: item) } func updateUIViewController(_ uiViewController: TestImageDisplay, context: Context) { guard uiViewController.item.id != item.id else { return } uiViewController.item = item uiViewController.loadImage() } }
3. 针对UIImage(contentsOfFile:)的优化
如果不用SDWebImage,手动加载图片时需异步处理+尺寸压缩,同时同样在销毁时清空图片:
// 替换TestImageDisplay中的loadImage方法 private func loadImage() { guard let filePath = item.imageURL.path else { return } DispatchQueue.global(qos: .userInitiated).async { // 加载并压缩图片到目标尺寸 guard let originalImage = UIImage(contentsOfFile: filePath), let resizedImage = originalImage.resized(to: CGSize(width: 200, height: 200)) else { return } DispatchQueue.main.async { self.imageView.image = resizedImage } } } // UIImage扩展:压缩图片 extension UIImage { func resized(to size: CGSize) -> UIImage? { let renderer = UIGraphicsImageRenderer(size: size) return renderer.image { context in self.draw(in: CGRect(origin: .zero, size: size)) } } }
额外优化建议
- SDWebImage缓存配置:设置合理的内存缓存上限,开启自动清理过期数据:
let cache = SDWebImageManager.shared.imageCache cache.config.maxMemoryCost = 100 * 1024 * 1024 // 100MB内存缓存上限 cache.config.shouldRemoveExpiredDataOnDisk = true - SwiftUI滚动行为优化:确保ScrollView使用合适的滚动目标行为,让LazyHStack正确卸载不可见视图:
ScrollView(.horizontal, showsIndicators: false) { LazyHStack(spacing: 16) { // ... 原有代码 } .scrollTargetLayout() } .scrollTargetBehavior(.viewAligned) // 确保视图对齐滚动,触发LazyHStack的视图卸载 - 避免循环引用:如果在闭包中引用
self,必须使用weak self或unowned self,防止内存泄漏。
内容的提问来源于stack exchange,提问作者LambentLight
相关产品推荐
相关产品推荐

