I have some code like this:
@MainActor
class Test {
fileprivate var value: Int?
func tryMe() {
Task {
self.value = nil
}
}
}
This code compiles properly, but my expectation was that, since the Test
class is annotated to run only on the main actor, and Task
would create a separate task, I would need to write:
await self.value = nil
Not only do I not need that - I get a warning if I try that ("No 'async' expressions occur within 'await' expression").
I think I might be missing something fundamental here. Is the code inside my Task
closure still running on the main actor? Is it only Task.detached
that will get me a truly separate task?
Yeah, that's exactly right, if you look at the Task initialiser you'll find an annotation called @_inheritActorContext
. That means that the closure you pass into the initialiser is isolated the current actor context – and since you annotated your class with @MainActor
, that actor context is the main actor. Furthermore, since the annotation is on the whole class, you value
property is also isolated to the main actor, which is why you don't need to mark await
when accessing it – both the closure inside the Task initialiser and the property are isolated to the main actor.
However, if you look at Task.detached
, you'll see that there isn't that @_inheritActorContext
annotation, meaning that the closure that you pass into the parameter isn't isolated to the main actor, unlike your value
property – which is why you'd need to mark an await
when accessing it.
4 Likes