语境
在一个使用Swift 5.x和Xcode14构建的Mac应用程序中,我有一个控制器对象.该对象具有SwiftUI视图可以观察到的几个@Published
个属性,因此我将该对象放置在@MainActor
上,如下所示:
@MainActor
final class AppController: NSObject, ObservableObject
{
@Published private(set) var foo: String = ""
@Published private(set) var bar: Int = 0
private func doStuff() {
...
}
}
问题
这个应用程序需要在Mac进入睡眠状态时采取某些操作,所以我订阅了init()
方法中的适当通知,但因为AppController
是用@MainActor
装饰的,所以我收到了这样的警告:
override init()
{
NSWorkspace.shared.notificationCenter.addObserver(forName: NSWorkspace.willSleepNotification, object: nil, queue: .main) { [weak self] note in
self?.doStuff() // "Call to main actor-isolated instance method 'doStuff()' in a synchronous nonisolated context; this is an error in Swift 6"
}
}
所以,我试着把它分离出来.但是(当然)编译器有一些新的东西要抱怨.这一次是一个错误:
override init()
{
NSWorkspace.shared.notificationCenter.addObserver(forName: NSWorkspace.willSleepNotification, object: nil, queue: .main) { [weak self] note in
Task { @MainActor in
self?.doStuff() // "Reference to captured var 'self' in concurrently-executing code
}
}
}
所以我这样做是为了解决这个问题:
override init()
{
NSWorkspace.shared.notificationCenter.addObserver(forName: NSWorkspace.willSleepNotification, object: nil, queue: .main) { [weak self] note in
let JUSTSHUTUP: AppController? = self
Task { @MainActor in
JUSTSHUTUP?.doStuff()
}
}
}
问题
最后一位不会产生编译器错误,并且似乎可以正常工作.但我不知道这是正确还是最好的做法.
我确实理解编译器为什么抱怨,以及它试图保护我免受的影响,但试图在现有项目中采用Swift并发是……痛苦的.