Ответ
Ответ зависит от контекста выполнения async-метода.
1. Когда переключение НЕ нужно:
Если async-метод или вызывающий его тип помечен аннотацией @MainActor, то весь код внутри этого метода и последующие вызовы уже выполняются на главном потоке.
@MainActor
func fetchUserData() async -> UserData { ... }
override func viewDidLoad() {
super.viewDidLoad()
Task {
let data = await fetchUserData() // Выполняется на MainActor
updateUI(with: data) // Безопасное обновление UI
}
}
2. Когда переключение НУЖНО:
Если async-метод не изолирован на MainActor (например, выполняет сетевой запрос или тяжелые вычисления), то для обновления UI необходимо явно переключиться.
// Этот метод не помечен @MainActor
func fetchFromNetwork() async -> Data { ... }
override func viewDidLoad() {
super.viewDidLoad()
Task {
let rawData = await fetchFromNetwork() // Может выполняться на фоновом потоке
// ОШИБКА: Прямое обновление UI здесь небезопасно
// self.label.text = String(data: rawData)
// ПРАВИЛЬНО: Явное переключение
await MainActor.run {
self.updateUI(with: rawData) // Теперь на главном потоке
}
}
}
Практическое правило:
- Проверяй, помечен ли вызываемый метод или его класс как
@MainActor. - Любое изменение свойств UIKit/SwiftUI должно происходить на главном потоке.
- Используй
await MainActor.run { ... }для явного переключения контекста, если не уверен. - Современный подход — аннотировать методы, обновляющие UI, как
@MainActor, чтобы компилятор контролировал потоковую безопасность.