【发布时间】:2022-06-11 12:27:32
【问题描述】:
在使用新的 Swift 并发工具时发现了这个问题。
设置如下:
class FailedDeinit {
init() {
print(#function, id)
task = Task {
await subscribe()
}
}
deinit {
print(#function, id)
}
func subscribe() async {
let stream = AsyncStream<Double> { _ in }
for await p in stream {
print("\(p)")
}
}
private var task: Task<(), Swift.Error>?
let id = UUID()
}
var instance: FailedDeinit? = FailedDeinit()
instance = nil
在 Playground 中运行此代码会产生以下结果:
init() F007863C-9187-4591-A4F4-BC6BC990A935
!!! deinit 方法永远不会被调用!!!
奇怪的是,当我把代码改成这样的时候:
class SuccessDeinit {
init() {
print(#function, id)
task = Task {
let stream = AsyncStream<Double> { _ in }
for await p in stream {
print("\(p)")
}
}
}
deinit {
print(#function, id)
}
private var task: Task<(), Swift.Error>?
let id = UUID()
}
var instance: SuccessDeinit? = SuccessDeinit()
instance = nil
通过直接在Task中移动subscribe()方法中的代码,控制台中的结果变为:
init() 0C455201-89AE-4D7A-90F8-D6B2D93493B1
deinit 0C455201-89AE-4D7A-90F8-D6B2D93493B1
这可能是一个错误,但肯定有一些我不明白的地方。我欢迎任何有关这方面的见解。
~!~!~!~!
这很疯狂(或者我可能是?),但使用 SwiftUI macOS 项目。我仍然没有和你一样的行为。查看我保留 FailedDeinit 和 SuccessDeinit 类的相同定义但在 SwiftUI 视图中使用它们的代码。
struct ContentView: View {
@State private var failed: FailedDeinit?
@State private var success: SuccessDeinit?
var body: some View {
VStack {
HStack {
Button("Add failed") { failed = .init() }
Button("Remove failed") { failed = nil }
}
HStack {
Button("Add Success") { success = .init() }
Button("Remove Success") { success = nil }
}
}
}
}
class FailedDeinit {
init() {
print(#function, id)
task = Task { [weak self] in
await self?.subscribe()
}
}
deinit {
print(#function, id)
}
func subscribe() async {
let stream = AsyncStream<Double> { _ in }
for await p in stream {
print("\(p)")
}
}
private var task: Task<(), Swift.Error>?
let id = UUID()
}
【问题讨论】:
-
这听起来很有趣,但请在真正的应用程序中进行测试,而不是在 Playground 中进行,因为 Playground 无法正确模拟内存管理(或异步/等待,就此而言)。
-
我第一次在真正的 macOS 应用程序中工作时偶然发现了这个问题,但试图在这种环境中找到问题的解决方案并不实际。
-
但是突然发现这一切都发生在 SwiftUI 项目的 State 变量中,你已经完全转移了目标。这不公平。我回答了你实际提出的问题。没有问出你真正想知道答案的问题是你自己做的。
-
哦,天哪……我不是故意冒犯或类似的。只是有时,提出问题并不像看起来那么容易。老实说,真正的问题出现在一个没有什么特别之处且不涉及 SwiftUI 的类中。该课程和应用程序的其余部分非常复杂,我试图通过在操场上工作来隔离问题,由于结果是相同的,我从未怀疑过操场。在那之后,我仍然想保持问题隔离,我构建了一个小型 SwiftUI 应用程序来测试你的想法,只是报告说问题仍然没有解决。
标签: swift async-await concurrency task asyncstream