iOS 17下SwiftUI View及成员无法释放的内存泄漏问题
SwiftUI fullScreenCover 视图关闭后无法释放的内存泄漏问题
问题复现
以下代码在iOS 17及以上版本中,反复弹出并关闭CheckSecond视图时,控制台仅打印init,不会触发LogDeinit的deinit方法,内存图谱显示LogDeinit实例被SwiftUI内部对象持续引用,存在内存泄漏;但该代码在iOS 16中表现正常,每次关闭视图都会打印deinit。
@main struct ExampleApp: SwiftUI.App { @State var show = false var body: some Scene { WindowGroup { VStack { Button("Present") { show = true } Text("First") .fullScreenCover(isPresented: $show) { CheckSecond { show = false } } } } } } struct CheckSecond: View { private let log = LogDeinit() var action: () -> Void var body: some View { Text("Second") Button("Back"){ action() } } } class LogDeinit { init() { print("init") } deinit { print("deinit") } }
问题原因
原代码通过传递闭包action来控制弹窗关闭,该闭包捕获了ExampleApp中的@State变量show,在iOS 17+的SwiftUI内部实现中,这种闭包捕获方式可能引发循环引用,导致CheckSecond视图及其成员无法被正确释放。
解决方案
避免通过闭包传递关闭逻辑,改用@Binding直接绑定弹窗的显示状态,让子视图直接控制绑定变量的取值,消除闭包捕获带来的引用问题:
@main struct ExampleApp: SwiftUI.App { @State var show = false var body: some Scene { WindowGroup { VStack { Button("Present") { show = true } Text("First") .fullScreenCover(isPresented: $show) { CheckSecond(isPresented: $show) } } } } } struct CheckSecond: View { private let log = LogDeinit() @Binding var isPresented: Bool var body: some View { Text("Second") Button("Back"){ isPresented = false } } } class LogDeinit { init() { print("init") } deinit { print("deinit") } }
修改后,每次关闭CheckSecond视图时,控制台会正常打印deinit,内存泄漏问题得到解决。
内容的提问来源于stack exchange,提问作者Dmitriy Kurkin
相关产品推荐
相关产品推荐

