SwiftUI List简单场景下内存泄漏问题排查求助
SwiftUI List 单元格内存管理探究问题
问题简述
环境:macOS 12.6.1、Xcode 14.2、iOS 16.2
为探究SwiftUI List在单元格显示/隐藏时的分配与释放逻辑,创建了演示项目,包含以下组件:
- Payload:数据模型,包含
id: Int和image: Image - PayloadViewModel:管理
[Payload],实例化时生成300个Payload对象(id从0到299),每个对象的image是100×100的彩色随机噪点图 - ContentView:通过
PayloadRow在List中展示viewModel.payloads数据
预期行为
- 应用启动后,
PayloadViewModel实例化并加载所有图片到数组,内存占用应约为52 MB - 滚动
List时内存不应显著增长,因为只需保留300个Payload对象,加上5-6个可见单元格的额外实例,且List仅复用可见单元格
实际行为
- 应用刚启动内存约20 MB,
PayloadViewModel生成完所有Payload后内存为26.3 MB,未达到预期的52 MB - 滚动列表时内存持续增长,滚动到底部时达到52 MB,且非可见单元格未被释放
Instruments分析
使用Allocations工具分析:
- 启动应用等待30秒,待所有
Payload生成后(不滚动),筛选最大尺寸的持久化对象,发现VM: CG raster data占用内存最大 - 滚动到最后一个单元格时,VM: CoreAnimation占用内存与CG raster data相当
当前猜想
应用刚启动时内存较低,可能是图片以压缩格式存储在内存中,显示时才解压为实际尺寸;但滚动时内存持续增长的原因尚不明确。
复现步骤
方式一:创建新项目,替换ContentView为下方代码
方式二:执行以下命令:
git clone git@github.com:DmytroKoval8/SwiftUIListExperiment.git cd SwiftUIListExperiment git checkout images-for-viewmodel-generated-randomly
也可切换到从资源库加载图片的分支:
git checkout images-for-viewmodel-loaded-from-assets
演示代码
import SwiftUI import Combine class PayloadViewModel: ObservableObject { @Published var payloads: [Payload] = [] init() { generatePayloads() } func generatePayloads() { DispatchQueue.global(qos: .userInitiated).async { [weak self] in guard let self = self else { return } for id in 0..<300 { let uiImage = self.generateRandomImage(width: 100, height: 100) let image = Image(uiImage: uiImage) let payload = Payload(id: id, image: image) DispatchQueue.main.async { self.payloads.append(payload) } print("Image \(id) generated") } } } func generateRandomImage(width: Int, height: Int) -> UIImage { let size = CGSize(width: width, height: height) UIGraphicsBeginImageContext(size) let context = UIGraphicsGetCurrentContext()! for y in 0..<height { for x in 0..<width { let color = UIColor( red: CGFloat(arc4random_uniform(256)) / 255.0, green: CGFloat(arc4random_uniform(256)) / 255.0, blue: CGFloat(arc4random_uniform(256)) / 255.0, alpha: 1.0 ) context.setFillColor(color.cgColor) context.fill(CGRect(x: x, y: y, width: 1, height: 1)) } } let image = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() return image } } struct Payload: Identifiable { let id: Int let image: Image } struct ContentView: View { @StateObject var viewModel = PayloadViewModel() var body: some View { List(viewModel.payloads) { payload in PayloadRow(payload: payload) .onAppear { print("Appear: \(payload.id)") } .onDisappear { print("Disappear: \(payload.id)") } } } } struct PayloadRow: View { let payload: Payload var body: some View { HStack { payload.image .resizable() .frame(width: 100, height: 100) Text("Payload \(payload.id)") } } }
内容的提问来源于stack exchange,提问作者SomeUser
相关产品推荐
相关产品推荐

