在 Swift 6 中获取“‘() async -> ()’类型的任务隔离值作为强传输参数传递”

问题描述 投票:0回答:1

在我的 Swift 5 项目中,我有这个扩展来将

async
函数传递给
Publisher.map
:

import Combine

public extension Publisher {
    func asyncMap<T>(
        _ asyncFunc: @escaping (Output) async -> T
    ) -> Publishers.FlatMap<Future<T, Never>, Self> {
        flatMap { value in
            Future { promise in
                Task {
                    let result = await asyncFunc(value)
                    promise(.success(result))
                }
            }
        }
    }
}

但是,我无法使用 Swift 6 在 Xcode 16.1 beta 中编译它,得到:

“‘() async -> ()’类型的任务隔离值作为强传输参数传递;以后的访问可能会出现竞争”。

是否可以将此扩展迁移到 Swift 6?已经尝试在各处添加

Sendable
@Sendable

ios swift async-await combine swift6
1个回答
0
投票

如果需要

asyncMap
才能在
@MainActor
上运行,并且
T
Sendable
,那么它就很简单并且可以正常工作:

public extension Publisher {
    @MainActor func asyncMap<T: Sendable>(
        _ asyncFunc: @escaping (Output) async -> T
    ) -> Publishers.FlatMap<Future<T, Never>, Self> {
        return flatMap { value in
            Future { promise in
                Task {
                    let result = await asyncFunc(value)
                    promise(.success(result))
                }
            }
        }
    }
}

如果不是……我不相信在不使用通用逃生舱口的情况下这是可能的(我稍后会展示它)。问题是 Future 没有标记它的

promise
参数是
@Sendable
sending
或任何东西。它不承诺不保留它并在某个随机点运行它,这可能会导致竞争。它不会这样做,但它没有承诺。而且我认为,如果苹果不更新Combine,就没有任何方法可以真正解决这个问题(他们似乎已经放弃了这一点)。

总有通用的逃生舱口:

@unchecked Sendable
,再加上标记几乎所有其他东西
@Sendable
将使这项工作成功:

struct UncheckedSendable<T>: @unchecked Sendable {
    let unwrap: T
    init(_ value: T) { unwrap = value}
}

public extension Publisher where Output: Sendable {
    func asyncMap<T: Sendable>(
        _ asyncFunc: @escaping @Sendable (Output) async -> T
    ) -> Publishers.FlatMap<Future<T, Never>, Self> {
        flatMap { value in
            Future { promise in
                let promise = UncheckedSendable(promise)
                Task {
                    let result = await asyncFunc(value)
                    promise.unwrap(.success(result))
                }
            }
        }
    }
}

对不起。

根据我们对

Future
的了解,我相信这实际上是安全的。许多 Swift 6 称之为不安全的事情实际上都是不安全的,我们只是忽略它,因为“那不会发生”(旁白:有时会发生)。但我相信我们确实知道这是安全的。苹果只是还没有更新Combine 来标记它。

© www.soinside.com 2019 - 2024. All rights reserved.