carlhung
(Carlhung)
1
protocol Po {
@MainActor
func foo()
}
class Base: Po {
var str = ""
func foo() {
str = "w"
print("foo: \(Thread.isMainThread)") // true
}
}
if #available(macOS 10.15, *) {
Task {
let b = Base()
await b.foo()
}
}
RunLoop.main.run()
while true {}
I don't understand why I can modify the property directly in the method that's marked with MainActor?
Because it seems wrong to me.
I believe Base and its property don't run on MainActor.
You create your Task at the top level, which implicitly makes it run on the main actor. Since Base is just a class (not an actor), its methods are run in whatever context its callers are in, which is the main actor in this case.
carlhung
(Carlhung)
3
Sorry, I am asking why foo can directly change the property of str
Why wouldn't it be able to?
carlhung
(Carlhung)
5
Because foo on main thread. But Base shouldn’t be? Because I didn’t mark Base with MainActor
You don't have to. Properties and methods can individually be associated with specific actors, including as part of protocol requirements. In this case foo is implicitly @MainActor because the Po protocol declares it as such.
It is perhaps a little too magical - foo is also implicitly async even though it's never actually marked as such, even in the original protocol declaration.
To test this further, if you add to Base e.g.:
func bar() {
foo()
}
…you'll get the compiler error Call to main actor-isolated instance method 'foo()' in a synchronous nonisolated context, demonstrating that the compiler is treating foo as @MainActor (but that the rest of Base isn't).
carlhung
(Carlhung)
7
Yes, I know foo is on MainActor.
But class Base it isn’t on the MainActor.
So, I would expect str = “xxx” in foo would show an error to me. Because they are on different thread. As my understanding. I may be wrong. Please correct me.