2025-02-08 18:06:06
,某些文章具有时效性,若有错误或已失效,请在下方留言。Apple explicitly annotates many of its types as being @MainActor
, including most UIKit types such as UIView
and UIButton
. However, for a while the Swift team dabbled with the idea of making some types gain main-actor-ness implicitly through a process called global actor inference – Swift applied @MainActor
automatically based on a set of predetermined rules.
Apple 将其许多类型显式批注为 @MainActor
,包括大多数 UIKit 类型,如 UIView
和 UIButton
。然而,有一段时间,Swift 团队尝试通过一个称为全局参与者推理的过程来隐式地使某些类型获得主要参与者身份 – Swift 根据一组预先确定的规则自动应用@MainActor
。
Global actor inference was enabled from Swift 5.5 through to Swift 5.10. It is disabled in Swift 6 language mode, so if you’re building using Swift 6 language mode or later you can ignore all of the below.
从 Swift 5.5 到 Swift 5.10 都启用了全局参与者推理。它在 Swift 6 语言模式下被禁用,因此如果您使用 Swift 6 语言模式或更高版本进行构建,则可以忽略以下所有内容。
Still here? 还在这里?
Okay, there are five rules for global actor inference, and I want to tackle them individually because although they start easy they get more complex.
好的,全局参与者推理有五条规则,我想单独处理它们,因为虽然它们开始时很容易,但它们会变得更加复杂。
First, if a class is marked @MainActor
, all its subclasses are also automatically @MainActor
. This follows the principle of least surprise: if you inherit from a @MainActor
class it makes sense that your subclass is also @MainActor
.
首先,如果一个类被标记为 @MainActor
,则它的所有子类也会自动@MainActor
。这遵循了最小意外原则:如果您从 @MainActor
类继承,则您的子类也是@MainActor
的。
Second, if a method in a class is marked @MainActor
, any overrides for that method are also automatically @MainActor
. Again, this is a natural thing to expect – you overrode a @MainActor
method, so the only safe way Swift can call that override is if it’s also @MainActor
.
其次,如果类中的方法标记为 @MainActor
,则该方法的任何覆盖也会自动@MainActor
。同样,这是很自然的事情 —— 你覆盖了一个 @MainActor
方法,所以 Swift 唯一可以调用该覆盖的安全方式是它也被@MainActor
。
Third, any struct or class using a property wrapper with @MainActor
for its wrapped value will automatically be @MainActor
. In SwiftUI, this makes @StateObject
and @ObservedObject
convey main-actor-ness on SwiftUI views that use them – if you use either of those two property wrappers in a SwiftUI view, the whole view becomes @MainActor
too.
第三,任何使用属性包装器(@MainActor
表示其包装值)的结构或类都将自动@MainActor
。在 SwiftUI 中,这使得@StateObject
和@ObservedObject
在使用它们的 SwiftUI 视图上传达主要参与者身份 – 如果您在 SwiftUI 视图中使用这两个属性包装器中的任何一个,则整个视图也会变得@MainActor
。
The fourth rule is where the difficulty ramps up a little: if a protocol declares a method as being @MainActor
, any type that conforms to that protocol will have that same method automatically be @MainActor
unless you separate the conformance from the method.
第四条规则是难度稍微增加的地方:如果协议声明一个方法@MainActor
,则符合该协议的任何类型都将自动@MainActor
相同的方法,除非你将一致性与方法分开。
What this means is that if you make a type conform to a protocol with a @MainActor
method, and add the required method implementation at the same time, it is implicitly @MainActor
. However, if you separate the conformance and the method implementation, you need to add @MainActor
by hand.
这意味着,如果使用 @MainActor
方法使类型符合协议,并同时添加所需的方法实现,则会隐式@MainActor
。但是,如果将一致性和方法实现分开,则需要手动添加@MainActor
。
Here’s that in code: 代码中的内容如下:
// A protocol with a single `@MainActor` method.
protocol DataStoring {
@MainActor func save()
}
// A struct that does not conform to the protocol.
struct DataStore1 { }
// When we make it conform and add save() at the same time, our method is implicitly @MainActor.
extension DataStore1: DataStoring {
func save() { } // This is automatically @MainActor.
}
// A struct that conforms to the protocol.
struct DataStore2: DataStoring { }
// If we later add the save() method, it will *not* be implicitly @MainActor so we need to mark it as such ourselves.
extension DataStore2 {
@MainActor func save() { }
}
As you can see, we need to explicitly use @MainActor func save()
in DataStore2
because the global actor inference does not apply there. Don’t worry about forgetting it, though – Xcode will automatically check the annotation is there, and offer to add @MainActor
if it’s missing.
如您所见,我们需要在 DataStore2 中显式使用 func save() @MainActor
因为全局 actor 推断不适用于 DataStore2
。不过,不要担心忘记它 – Xcode 会自动检查注释是否存在,如果缺少注释,则提供添加@MainActor
。
The fifth and final rule is most complex of all: if the whole protocol is marked with @MainActor
, any type that conforms to that protocol will also automatically be @MainActor
unless you put the conformance separately from the main type declaration, in which case only the methods are @MainActor
.
第五条也是最后一条规则是最复杂的:如果整个协议都标有 @MainActor
,则符合该协议的任何类型也将自动@MainActor
,除非您将一致性与主类型声明分开,在这种情况下,仅@MainActor
方法。
In attempt to make this clear, here’s what I mean:
为了弄清楚这一点,我的意思是:
// A protocol marked as @MainActor.
@MainActor protocol DataStoring {
func save()
}
// A struct that conforms to DataStoring as part of its primary type definition.
struct DataStore1: DataStoring { // This struct is automatically @MainActor.
func save() { } // This method is automatically @MainActor.
}
// Another struct that conforms to DataStoring as part of its primary type definition.
struct DataStore2: DataStoring { } // This struct is automatically @MainActor.
// The method is provided in an extension, but it's the same as if it were in the primary type definition.
extension DataStore2 {
func save() { } // This method is automatically @MainActor.
}
// A third struct that does *not* conform to DataStoring in its primary type definition.
struct DataStore3 { } // This struct is not @MainActor.
// The conformance is added as an extension
extension DataStore3: DataStoring {
func save() { } // This method is automatically @MainActor.
}
I realize that might sound obscure, but it makes sense if you put it into a real-world context. For example, let’s say you were working with the DataStoring
protocol we defined above – what would happen if you modified one of Apple’s types so that it conformed to it?
我知道这听起来可能很晦涩难懂,但如果你把它放在现实世界的背景下,它是有道理的。例如,假设您正在使用我们上面定义的 DataStoring
协议 – 如果您修改了 Apple 的其中一种类型以使其符合该协议,会发生什么情况?
If conformance to a @MainActor
protocol retroactively made the whole of Apple’s type @MainActor
then you would have dramatically altered the way it worked, probably breaking all sorts of assumptions made elsewhere in the system.
如果符合 @MainActor
协议追溯性地使整个 Apple 的类型@MainActor
那么你将极大地改变它的工作方式,可能会打破系统中其他地方所做的各种假设。
If it’s your type – a type you’re creating from scratch in your own code – then you can add the protocol conformance as you make the type and therefore isolate the entire type to @MainActor
, because it’s your choice.
如果这是您的类型(您在自己的代码中从头开始创建的类型),那么您可以在创建类型时添加协议一致性,从而将整个类型隔离到@MainActor
,因为这是您的选择。