Hello, I have a next code and I have an error on Task B Mutation of captured var 'z' in concurrently-executing code.
static func main() async {
var z = 1
let r = Task { // Task A
z = 4
return 0
}
let k = Task.detached() { // Task B
z = 4 // Mutation of captured var 'z' in concurrently-executing code
return 0
}
let m = await k.value
print("n z: \(z)")
}
Please, explain, why there is no error in Task A. Because, it is also executed in parallel with the main() function and it has a concurrently-executing code🤯
The main function is implicitly @MainActor (when contained in an @main type). Task A inherits the actor context of the caller, so it's @MainActor as well.
This means that the calling function and Task A are not actually executing in parallel.
The detached Task on the other hand does not inherit the actor context. It is executing in parallel and thus can't safely access z.
class TestController: UIViewController {
override func viewDidLoad() {
Task { // Task A
return 0
}
}
}
class TestController: UIViewController {
var z = 0
let button = UIButton(type: .close)
override func viewDidLoad() {
view.addSubview(button)
button.addTarget(self, action: #selector(tap), for: .touchUpInside)
}
@objc func tap() {
Task { // Task A
z = 4
return 0
}
}
}
The 'Task A' will be executed in parallel with the main thread, will not be executed at all, or will be executed on the main thread after all operations (started by last user input) have been completed?
As you can see from its declaration UIViewController is @MainActor:
@MainActor class UIViewController : UIResponder
Both "Task A" examples inherit @MainActor from their surrounding actor context. Both tasks are executing on the main thread.
after all operations (started by last user input) have been completed?
That depends on what you call "operations":
Yes, currently executing non-async code will continue to execute before any other task can run on the main thread. (Tasks use cooperative multitasking.)
No, if the "operation" on the main thread is async, it might be interrupted at an await and your Task might run before the operation finishes.
Since your code is not async it can't be interrupted.
And if we started our task on main thread and it is waiting a big download task now, could an user input interrupt its waiting and revive the main thread?
When you define a variable in an async context. It is known as a task local variable.
That variable can only be captured if it’s sendable, or if it is being accessed from the same task context.
In this case variable z is defined in the task context of the parent function. When you create a detached task, it does not inherit that task context. So you cannot mutate variable.
In this case, you can use a regular task to solve this problem.