错误:将 [StructType] 对 [ProtocolType] 的符合性跨入主Actor隔离的代码,在易于理解的并发模型中
我已经开启了approachable concurrency,默认以主Actor作为隔离。我有这段代码:
protocol RLActionState: CustomDebugStringConvertible {}
struct RLWaitActionState: RLActionState {}
我有一个警告:
对协议 'RLActionState' 的RLWaitActionState的一致性跨入了主Actor隔离的代码,可能导致数据竞争;在Swift 6语言模式下这是一个错误。
然后我发现这段代码可以工作:
struct RLWaitActionState: @MainActor RLActionState {}
而这段则不起作用:
@MainActor
extension RLWaitActionState: RLActionState {}
我的问题是:
- 默认情况下,一切都应该在主Actor中,那么为什么还需要 @MainActor?
@MainActor extension Foo: Protocol {}与extension Foo: @MainActor Protocol{}有何区别,为什么一个能工作、一个会失败。
我看过 为什么 @Model对 PersistentModel和 Hashable的符合会跨入Swift 6.2的 @MainActor隔离? ,但它们不是同一个用例。
Playground中的完整示例:
import UIKit
@MainActor
protocol RLActionImpl {}
@MainActor
struct RLWaitActionImpl: RLActionImpl, @MainActor CustomDebugStringConvertible {
let duration: TimeInterval
var debugDescription: String {
"wait(\(duration))"
}
}
@MainActor
protocol RLActionState: CustomDebugStringConvertible {
associatedtype Impl: RLActionImpl
var action: Impl { get }
var key: String? { get }
}
@MainActor
extension RLActionState {
var debugDescription: String {
if let key {
"\(key)->\(action)"
} else {
"\(action)"
}
}
}
@MainActor
struct RLWaitActionState: RLActionState {
typealias Impl = RLWaitActionImpl
let action: RLWaitActionImpl
let key: String?
}
解决方案
这里的关键点在于 CustomDebugStringConvertible 是一个非隔离协议。因此,RLActionState 也是非隔离的。
“默认以主Actor”为准并不适用于 RLActionState,因为它继承自一个非隔离协议。但它确实适用于 RLWaitActionState——RLWaitActionState 的所有成员都是主Actor隔离的,这并不符合该协议对非隔离的要求。
这就是你仍然需要像下面这样进行隔离符合性来让它编译的原因:
struct RLWaitActionState: @MainActor RLActionState {}
另一方面,这个:@MainActor
extension RLWaitActionState: RLActionState {} …完全是多余的。默认的“主Actor”设置本来就会把这个 @MainActor 属性应用到你所有的扩展上。这里的 @MainActor 只是让扩展中的所有成员都成为主Actor隔离。
这与 isolated conformance 的概念不同,后者允许你用全局Actor隔离的成员来满足本应非隔离的协议要求。作为代价,你就不能自由地使用这类类型的值。有关更多细节,请参阅链接中的SE提案。