我对 Swift 比较陌生,并试图做一个最小的 GlobalActor 工作,应该在专用线程上运行代码,使用
unownedExecutor
中建议的 docs:
@globalActor actor BackendActor: GlobalActor {
static let shared = BackendActor()
nonisolated let unownedExecutor: UnownedSerialExecutor = Executor().asUnownedSerialExecutor()
final private class Executor: SerialExecutor {
private let dispatcher = DispatchQueue(label: "BackendQueue")
internal func enqueue(_ job: UnownedJob) {
self.dispatcher.async {
job.runSynchronously(on: sharedUnownedExecutor)
}
}
internal func asUnownedSerialExecutor() -> UnownedSerialExecutor {
UnownedSerialExecutor(ordinary: self)
}
}
}
运行以下 MWE 时,
import SwiftUI
@main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
var body: some View {
Button("Test") {
Task.detached { @BackendActor in
print("Ok")
}
}
}
}
并按下按钮
Test
,EXC_BAD_ACCESS
指令中会出现 Task.detached
错误。
我用网上可以找到的示例多次检查了我的
GlobalActor
的代码,但我没有发现它可能无法工作的任何区别。
正如
unownedExecutor
的 XCode 中的文档所建议的,此属性对于 actor 实例必须相同,并且 actor 必须保持执行器处于活动状态,如您的代码中所示,我检查 Executor
是否已 deinit
所以发生了异常。
解决方案可以将 Excutor 实例保留为 actor 中的属性以使其保持活动状态:
@globalActor actor BackendActor {
static let shared = BackendActor()
fileprivate let executor = Executor()
nonisolated var unownedExecutor: UnownedSerialExecutor {
executor.asUnownedSerialExecutor()
}
}
final private class Executor: SerialExecutor {
private let dispatcher = DispatchQueue(label: "BackendQueue")
static let shared = Executor()
func enqueue(_ job: UnownedJob) {
dispatcher.async {
job.runSynchronously(on: self.asUnownedSerialExecutor())
}
}
func asUnownedSerialExecutor() -> UnownedSerialExecutor {
UnownedSerialExecutor(ordinary: self)
}
}