You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何消除Xcode 15 Beta 8中Non-Sendable Notification跨Actor边界警告?

问题描述

我开发了一款Mac应用,需要在Mac进入睡眠状态时执行操作。为此,我采用以下「现代」方式监听通知:

@MainActor
final class AppController: NSObject, ObservableObject
{
   var sleepTask: Task<Void, Never>? = nil

   override init()
   {
       sleepTask = Task { [weak self] in
          for await _ in NSWorkspace.shared.notificationCenter.notifications(named: NSWorkspace.willSleepNotification) 
          {
              self?.doSomething()
          }
       }
   }
}

Xcode 15 Beta 8在for/await调用处新增了一条警告:

Non-sendable type 'Notification?' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary

  1. Generic enum 'Optional' does not conform to the 'Sendable' protocol (Swift.Optional)

但我根本没有使用该notification对象,它并未跨越Actor边界。

问询

如何消除这条警告?(除了强制让Optional遵循@unchecked Sendable之外的方法)


解决方法

这里提供几种无需修改Optional协议一致性的可行方案:

方案一:将监听逻辑移到非Main Actor上下文

NSWorkspace的通知中心属于非隔离(nonisolated)对象,我们可以把异步流的监听逻辑放到全局并发上下文,避免跨Actor的Sendable检查冲突,之后再切回Main Actor执行业务操作:

override init() {
    sleepTask = Task.detached { [weak self] in
        for await _ in NSWorkspace.shared.notificationCenter.notifications(named: NSWorkspace.willSleepNotification) {
            await MainActor.run {
                self?.doSomething()
            }
        }
    }
}

方案二:改用传统的NotificationCenter监听方式

如果不需要异步流的语法糖,退回到经典的addObserver写法,可以完全避开这个警告,同时兼容性更好:

@MainActor
final class AppController: NSObject, ObservableObject {
    private var observation: NSObjectProtocol?

    override init() {
        super.init()
        observation = NotificationCenter.default.addObserver(
            forName: NSWorkspace.willSleepNotification,
            object: nil,
            queue: .main
        ) { [weak self] _ in
            self?.doSomething()
        }
    }

    deinit {
        if let observation {
            NotificationCenter.default.removeObserver(observation)
        }
    }
}

方案三:自定义符合Sendable的异步流包装器

创建一个简单的扩展,把原API返回的Notification?流转换成只传递Void的符合Sendable的异步流,既保留现代异步写法,又规避原API的类型问题:

extension NotificationCenter {
    func sendableNotifications(named name: NSNotification.Name) -> AsyncStream<Void> {
        AsyncStream { continuation in
            let token = addObserver(forName: name, object: nil, queue: nil) { _ in
                continuation.yield()
            }
            continuation.onTermination = { _ in
                removeObserver(token)
            }
        }
    }
}

之后在代码中使用这个包装后的流:

sleepTask = Task { [weak self] in
    for await _ in NSWorkspace.shared.notificationCenter.sendableNotifications(named: NSWorkspace.willSleepNotification) {
        self?.doSomething()
    }
}

内容的提问来源于stack exchange,提问作者Bryan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 04:03:25