iOS替换UIImage时出现内存泄漏问题求助
内存攀升问题排查与解决
核心问题:UIImage(named:)的不当使用
你用UIImage(named: imageURL.path)加载Documents目录的图片,这个方法原本是为加载App Bundle内的资源图片设计的,它会自动将加载后的图片存入系统全局缓存,且该缓存不会随UIImage对象的释放自动清理。每次切换图片时,新图片都会被加入缓存,旧图片无法被回收,直接导致内存持续上涨。
修复方案
替换图片加载方式,使用不触发系统缓存的方法读取文件:
方案1:用UIImage(contentsOfFile:)直接读取
func replaceImage(with imageURL: URL) { assert(imageURL.isFileURL) // 直接从文件路径读取,不触发系统缓存 guard let nextImage = UIImage(contentsOfFile: imageURL.path) else { // 处理图片加载失败的场景 return } UIView.transition( with: self.imageView, duration: 0.1, options: .transitionCrossDissolve, animations: { self.imageView.image = nextImage }, completion: nil ) }
方案2:通过Data读取
如果需要更灵活的处理(比如验证文件完整性),也可以用Data读取:
func replaceImage(with imageURL: URL) { assert(imageURL.isFileURL) guard let imageData = try? Data(contentsOf: imageURL), let nextImage = UIImage(data: imageData) else { return } UIView.transition( with: self.imageView, duration: 0.1, options: .transitionCrossDissolve, animations: { self.imageView.image = nextImage }, completion: nil ) }
这两种方式都不会缓存图片,当imageView替换新图后,旧的UIImage对象会被ARC自动回收,内存就能正常释放。
额外注意事项
1. Timer的循环引用问题
你当前用Timer.scheduledTimer(target:)创建的重复Timer会持有self,如果视图控制器(VC)销毁时未手动终止Timer,会导致VC无法被回收,形成隐性内存泄漏。修复方式:
- 先将Timer保存为实例变量:
var slideTimer: Timer? - 在
viewDidLoad中初始化:slideTimer = Timer.scheduledTimer( timeInterval: self.cycleSeconds, target: self, selector: #selector(displayNext), userInfo: nil, repeats: true ) - 在
deinit中销毁Timer:deinit { slideTimer?.invalidate() }
或者直接用block式Timer+弱引用,从根源避免循环引用:
Timer.scheduledTimer(withTimeInterval: self.cycleSeconds, repeats: true) { [weak self] _ in self?.displayNext() }
2. 大图片内存优化
如果你的图片分辨率远超过屏幕尺寸,即使不缓存,单张图片也会占用大量内存。可以在加载时对图片进行降采样处理:
func downsampledImage(from url: URL, to targetSize: CGSize) -> UIImage? { let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary guard let imageSource = CGImageSourceCreateWithURL(url as CFURL, imageSourceOptions) else { return nil } let maxPixelSize = max(targetSize.width, targetSize.height) * UIScreen.main.scale let downsampleOptions = [ kCGImageSourceCreateThumbnailFromImageAlways: true, kCGImageSourceCreateThumbnailWithTransform: true, kCGImageSourceThumbnailMaxPixelSize: maxPixelSize ] as CFDictionary guard let downsampledCGImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions) else { return nil } return UIImage(cgImage: downsampledCGImage) }
在replaceImage中调用该方法,传入imageView的尺寸作为目标大小,大幅降低单张图片的内存占用。
内容的提问来源于stack exchange,提问作者pnadeau
相关产品推荐
相关产品推荐

