我想创建一个异步序列,每当我收到一组通知的通知时就会发出该序列(Xcode 16.1 RC)。我的想法是,最简单的方法是使用 Apple 的
merge()
包中的 swift-async-algorithms
方法。我最初的天真的尝试:
let note1 = NotificationCenter.default.notifications(named: .noteOne)
let note2 = NotificationCenter.default.notifications(named: .noteTwo)
let merged = merge(note1, note2) // ❗️ Conformance of 'Notification' to 'Sendable' is unavailable
这个错误是有道理的;我知道
Notification
不可能是 Sendable
,因为它具有 userInfo
属性。所以我想我可以使用将其映射到其他东西
let note1 = NotificationCenter.default.notifications(named: .noteOne)
.map { _ in () }
let note2 = NotificationCenter.default.notifications(named: .noteTwo)
.map { _ in () }
let merged = merge(note1, note2) // ❗️ Conformance of 'Notification' to 'Sendable' is unavailable
这行不通;我得到同样的编译错误。那么...我该怎么做?
添加
.map { _ in () }
不起作用,因为 AsyncMapSequence<Base, Transformed>
仅在满足所有这些条件时才符合 Sendable
:
Base
是 Sendable
Base.Element
是 Sendable
Transformed
是 Sendable
另请参阅源代码。
在这种情况下,第二个条件不满足。
Notification
不符合Sendable
。
从源代码来看,这个
Sendable
一致性实际上是@unchecked
。这是因为 AsyncMapSequence
被设计为采用非 Sendable
闭包(它将其存储在存储属性中),并且编译器无法判断这是安全的。
事实上,如果闭包是一个
@Sendable
闭包,那么只需要第一个条件。由于您的闭包仅映射到 ()
,因此它 is Sendable
,因此您可以编写自己的 AsyncMapSequence
来代替 @Sendable
闭包。
既然你的闭包也不是
async
,我写这个SyncMapSequence
是为了让事情变得简单,
public struct SyncMapSequence<Base, Transformed>: AsyncSequence where Base: AsyncSequence {
let f: @Sendable (Base.Element) -> Transformed
let base: Base
public struct AsyncIterator: AsyncIteratorProtocol {
var baseIterator: Base.AsyncIterator
let f: @Sendable (Base.Element) -> Transformed
public mutating func next() async throws -> Transformed? {
try await baseIterator.next().map(f)
}
}
public func makeAsyncIterator() -> AsyncIterator {
AsyncIterator(baseIterator: base.makeAsyncIterator(), f: f)
}
}
extension SyncMapSequence: Sendable where Base: Sendable {}
extension AsyncSequence {
func syncMap<Transformed>(transform: @Sendable @escaping (Element) -> Transformed) -> SyncMapSequence<Self, Transformed> {
SyncMapSequence(f: transform, base: self)
}
}
用途:
let note1 = NotificationCenter.default.notifications(named: .noteOne)
.syncMap { _ in () }
let note2 = NotificationCenter.default.notifications(named: .noteTwo)
.syncMap { _ in () }
let merged = merge(note1, note2)