【问题标题】:Swift Concurrency: Notification Callbacks on @MainActor ObjectsSwift 并发:@MainActor 对象上的通知回调
【发布时间】:2022-12-11 13:09:44
【问题描述】:

语境

在使用 Swift 5.x 和 Xcode 14 构建的 Mac 应用程序中,我有一个控制器对象。这个对象有几个 @Published 属性,SwiftUI 视图可以观察到这些属性,所以我把这个对象放在 @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 并发是......痛苦的。

【问题讨论】:

    标签: swift async-await appkit swift-concurrency


    【解决方案1】:

    您可以使用您的Task { @MainActor in ... } 模式,但将[weak self] 捕获列表添加到Task

    NSWorkspace.shared.notificationCenter.addObserver(
        forName: NSWorkspace.willSleepNotification,
        object: nil,
        queue: .main
    ) { [weak self] note in
        Task { @MainActor [weak self] in
            self?.doStuff()
        }
    }
    

    【讨论】:

      【解决方案2】:

      另一种方法是使用Combine

      import Combine
      
      @MainActor
      final class AppController: NSObject, ObservableObject
      {
          @Published private(set) var foo: String = ""
          @Published private(set) var bar: Int = 0
          
          private var cancellable : AnyCancellable?
          
          private func doStuff() {
              //
          }
          
          override init()
          {
              super.init()
              cancellable = NSWorkspace.shared.notificationCenter
                  .publisher(for: NSWorkspace.willSleepNotification)
                  .receive(on: DispatchQueue.main)
                  .sink { [weak self] note in
                      self?.doStuff()
                  }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-12
        • 1970-01-01
        • 1970-01-01
        • 2022-01-23
        • 2021-08-27
        • 1970-01-01
        相关资源
        最近更新 更多