Ответ
По умолчанию код выполняется на MainActor (главном потоке).
Модификатор .task создает асинхронную задачу, привязанную к жизненному циклу вью. Swift Conruntime автоматически запускает её в контексте MainActor, чтобы обеспечить безопасность работы с UI.
Пример и важные детали:
.task {
// Этот код запускается и выполняется на главном потоке.
isLoading = true
// Приостановка на `await`. Поток освобождается для других задач.
let data = await networkService.fetchData()
// Продолжение также выполняется на главном потоке.
self.items = data
isLoading = false
}
Как выполнить тяжелую работу в фоне?
Используйте Task.detached или явно выносите вычисления из контекста MainActor:
.task {
let heavyResult = await Task.detached(priority: .userInitiated) {
// Этот блок выполняется в фоновом потоке из пула.
return await performHeavyCalculation()
}.value
// Возвращаемся в главный поток для обновления состояния.
self.result = heavyResult
} Ответ 18+ 🔞
Ага, слушай, вот эта вся хуйня с .task — она же, блядь, по умолчанию на главном потоке сидит, как приклеенная! Потому что SwiftUI, этот хитрожопый фреймворк, сам всё подгоняет под MainActor, чтобы ты свою вьюху случайно не разнёс из другого потока.
Смотри, вот пример, который у тебя есть:
.task {
// Этот код запускается и выполняется на главном потоке.
isLoading = true
// Приостановка на `await`. Поток освобождается для других задач.
let data = await networkService.fetchData()
// Продолжение также выполняется на главном потоке.
self.items = data
isLoading = false
}
Всё, что до await — на главном. После await — тоже на главном. Конкурентность, блядь, магия, но безопасная. Поток не блокируется, пока ты ждёшь данные, но как только они приехали — продолжение летит обратно в UI-контекст. Удобно, ёпта.
А вот если тебе надо, например, ебнуть какую-то тяжёлую хуйню в фоне, чтобы интерфейс не тормозил? Ну, тут уже надо руками выносить. Смотри, как это делают умные люди:
.task {
let heavyResult = await Task.detached(priority: .userInitiated) {
// Этот блок выполняется в фоновом потоке из пула.
return await performHeavyCalculation()
}.value
// Возвращаемся в главный поток для обновления состояния.
self.result = heavyResult
}
Task.detached — это как сказать системе: «Слушай, дружок, вот эту поеботу гони в фоновый пул, мне на главном делать нехуй». А потом .value забираешь результат, и он автоматом возвращается в главный актор, потому что свойство result у вьюхи, скорее всего, изолировано под MainActor. Красота, в рот меня чих-пых!
Главное — не перепутай. Если будешь внутри .task без detached городить циклы на овердохуища итераций, то UI у тебя просто встанет колом, и пользователь подумает, что приложение накрылось медным тазом. А ты потом будешь чесать репу: «И чё это оно тормозит?»