I think the rule must be that a Task initializer in a MainActor method runs on the main thread.
And all methods of a view controller are MainActor methods by default; plus, I observe that if I declare test2
to be nonisolated
, its Task operation runs on a background thread instead of the main thread.
My guess, then, is that this is an example of the rule that a Task initializer's operation "inherits" from its context:
test2
is a MainActor method; it runs on the main thread, so the Task operation "inherits" that.
But test1
is not marked for any special thread. test1
itself runs on the main thread, because it is called on the main thread; but it is not marked to run on the main thread. Therefore its Task operation falls back to running on a background thread.
That's my theory, anyway, But I find it curious that this rule is nowhere clearly enunciated in the relevant WWDC videos.
Moreover, even test2
is only a MainActor method in a sort of "weak" way. If it were really a MainActor method, you could not be able to call it from a background thread without await
. But you can, as this version of the code shows:
func test1() {
print("test1", Thread.isMainThread) // true
Task {
print("test1 task", Thread.isMainThread) // false
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
test1()
Task.detached {
self.test2()
}
}
func test2() {
print("test2", Thread.isMainThread) // false
Task {
print("test2 task", Thread.isMainThread) // true
}
}
}
I find that truly weird, and I have some difficulty enunciating what rule would govern this relentless context-switching behavior, so I don't regard the matter as settled.