我想了解正确的用法Task { }
在以下 SwiftUI 代码中。我的目标是获得基本的了解以避免内存泄漏。
这是 SwiftUI 部分的示例代码:
struct MyView: View {
@ObservedObject var viewModel: ViewModel
var body: some View {
VStack {
Text(viewModel.publishedString)
}
.onAppear(perform: {
Task {
viewModel.resetUI()
await viewModel.doSomeAsyncStuff()
}
})
.onDisappear {
Task {
await viewModel.doAnotherAsyncStuff()
viewModel.resetUI()
}
}
}
}
The .onAppear { … }
and .onDisappear { … }
修饰符将调用 ViewModel 类的同步和异步函数。这Text(viewModel.publishedString)
代表一个已发布的属性(使用@Published
) 修改为viewModel.doSomeAsyncStuff()
and viewModel.doAnotherAsyncStuff()
.
请记住,我需要向后兼容 SwiftUI 2.0,这就是为什么我不使用.task { }
修饰符。
您能否确认我对本例中内存泄漏和保留周期的理解是否正确:
- 通话期间
Task { … }
,将在 Task 闭包内创建 MyView 类型值的副本
- 该结构类型值的副本保存对
ViewModel
class
- 保留参考
ViewModel
不分配新内存(因为它只是一个指针,而结构的副本将分配任务关闭完成后释放的新内存
- 对 ViewModel 类的函数调用以及已发布的更新
publishedString
Task 闭包中的属性将更新 SwiftUI 视图,因为 View 结构的所有副本都持有对 ViewModel 类的相同引用
- 完成任务关闭将取消分配视图结构的副本
- 如果初始 MyView 值(不是 Task 闭包内的副本)由于不再需要而必须从内存中取消分配,但 Task 闭包尚未完成,则仅在 ViewModel 引用之前保留强引用完成任务关闭。一旦任务关闭(在
.onAppear { … }
and .onDisappear { … }
)完成后,所有为 View 结构和 ViewModel 类引用分配的内存都将被释放。
@StateObject
旨在消除使用的需要onAppear
。该对象在视图第一次出现之前初始化,并在视图消失时取消初始化。所以你可以在对象的内部开始你的任务init
并取消它deinit
。注意,我们通常称这个对象为“Loader”而不是“View Model”,因为在 SwiftUI 中View
结构加号@State
已经是视图模型。
如果您想在任务每次出现时运行而不是第一次出现,那么您可以添加重新启动func
取消并重新启动任务,然后从onAppear
.
更新:我更多地考虑了这一点,我相信可以存储Task
处理在一个@State
从而重新实现类似的东西.task
:
struct TaskTestView: View {
@State var task: Task<Void, Never>?
var body: some View {
Text("Test")
.onAppear {
task = Task {
try? await Task.sleep(for: .seconds(3))
if Task.isCancelled {
print("cancelled")
return
}
print("complete")
}
}
.onDissapear {
task?.cancel()
}
}
}
用这个测试:
struct TaskTestView2: View {
@State var showing = false
var body: some View {
VStack {
if (showing) {
TaskTestView()
}
else {
Text("Not showing")
}
}
Button("Show/Hide") {
showing.toggle()
}
}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)