如何消除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
- 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
相关产品推荐
相关产品推荐

