为什么从Realm DB加载图片到SwiftUI List时会崩溃且RAM达到上限?
内存不释放的核心原因
你遇到的问题本质和SwiftUI List的复用机制无关,是代码逻辑导致图片实例被持续持有无法释放:
- 你提前把所有转换好的
Image实例存入了全局的imageList数组,这个数组会强持有所有Image对应的UIImage对象,就算List单元格滚出屏幕被复用,数组里的图片实例依然存在,内存自然不会下降 - 你直接将全尺寸的
Data转换为UIImage使用,没有做降采样,单张图片的内存占用极高(比如一张1080P的图片内存占用就有~8MB,7000张全加载的话要远超过手机可用内存) - List内嵌套横向
ScrollView时,你用普通HStack加载所有横向图片,相当于每个List行都会一次性加载该行所有图片,没有懒加载机制,进一步加快了内存上涨速度
解决思路
- 摒弃预生成全局
imageList的写法,改为按需加载:只在ForEach渲染单个图片的时候,实时从Realm读取对应Data、生成图片,不要全局持有所有图片实例,滚出可视区的单元格销毁后,对应的图片没有被强持有就会自动释放 - 强制做图片降采样,不要直接使用全尺寸UIImage,根据列表图片的实际显示尺寸生成对应大小的缩略图,内存占用可以降低90%以上,示例实现:
func downsampleImage(from data: Data, targetSize: CGSize) -> UIImage? { let scale = UIScreen.main.scale let maxPixel = max(targetSize.width, targetSize.height) * scale let options: [CFString: Any] = [ kCGImageSourceCreateThumbnailFromImageAlways: true, kCGImageSourceThumbnailMaxPixelSize: maxPixel, kCGImageSourceCreateThumbnailWithTransform: true ] guard let source = CGImageSourceCreateWithData(data as CFData, nil), let cgThumbnail = CGImageSourceCreateThumbnailAtIndex(source, 0, options as CFDictionary) else { return nil } return UIImage(cgImage: cgThumbnail) }
- 把横向ScrollView里的
HStack替换为LazyHStack,让横向滚动的图片也支持懒加载,避免每行一次性加载全部横向图片 - 修正ForEach的标识逻辑,不要用数组下标作为id,改为使用图片在Realm中存储的唯一主键作为id,保证List的单元格复用逻辑正常生效
- 可选添加内存缓存优化:用
NSCache存储降采样后的UIImage,设置合理的缓存上限(比如总内存限制100M、最多缓存200张图),NSCache会在系统内存紧张时自动释放缓存内容,既可以提升滚动流畅度,也不会出现内存溢出问题 - 尽量固定List的行高,避免动态高度导致List复用逻辑失效,iOS 16及以上版本可以配合
listRowSeparator(.visible)等Modifier明确List的布局规则,提升复用效率
内容的提问来源于stack exchange,提问作者MoLoW
相关产品推荐
相关产品推荐

