Что следует помечать MainActor?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что следует помечать MainActor в iOS-разработке
MainActor — это глобальный актор, гарантирующий выполнение кода на главной очереди (main thread). В iOS-разработке это критически важно, поскольку UI-операции должны выполняться исключительно на главном потоке. Несоблюдение этого правила приводит к крешам и неопределённому поведению интерфейса.
Когда использовать MainActor
1. UI-компоненты и View-модели
Любые объекты, непосредственно связанные с отображением или обновлением UI:
@MainActor
final class ProfileViewController: UIViewController {
// Весь код здесь выполняется на main thread
}
@MainActor
class SettingsViewModel: ObservableObject {
@Published var isEnabled: Bool = false // SwiftUI property wrapper
}
2. Свойства и методы, обновляющие UI
Даже если класс не целиком помечен @MainActor, можно аннотировать отдельные свойства/методы:
class DataLoader {
private var data: [String] = []
@MainActor
func updateTableView(_ tableView: UITableView) {
tableView.reloadData() // Безопасное обновление UI
}
}
3. SwiftUI-представления и StateObject
В SwiftUI многие конструкции уже автоматически выполняются на главном потоке, но явная аннотация улучшает читаемость:
@MainActor
struct ContentView: View {
@StateObject var viewModel = AppViewModel() // StateObject требует MainActor
var body: some View {
Text("Hello, World!")
}
}
4. Делегаты и колбэки, затрагивающие UI
Объекты, получающие обратные вызовы, которые должны обновлять интерфейс:
@MainActor
class DownloadDelegate: NSObject, URLSessionDownloadDelegate {
func urlSession(_ session: URLSession,
downloadTask: URLSessionDownloadTask,
didFinishDownloadingTo location: URL) {
// Обработка завершения загрузки с обновлением UI
updateProgressIndicator()
}
}
5. Общие ресурсы, используемые UI
Глобальные или shared-ресурсы, к которым обращаются из UI-контекста:
@MainActor
final class AppThemeManager {
static let shared = AppThemeManager()
private init() {}
var currentTheme: Theme = .light
}
Когда НЕ следует использовать MainActor
- Фоновые операции (сетевая загрузка, обработка изображений, вычисления)
- Потокобезопасные структуры данных, не связанные с UI
- Чисто бизнес-логические модули без UI-зависимостей
- Низкоуровневые утилиты (парсеры, кодировщики)
Практический пример архитектуры с MainActor
// Фоновая служба (без MainActor)
actor DataService {
func fetchData() async throws -> [DataModel] {
// Сетевая загрузка в фоне
}
}
// ViewModel с MainActor
@MainActor
final class ContentViewModel: ObservableObject {
@Published var items: [DataModel] = []
private let service = DataService()
func loadData() async {
do {
let data = try await service.fetchData()
// Автоматически возвращаемся на главный поток
self.items = data
} catch {
handleError(error)
}
}
}
Ключевые принципы
- Явность лучше неявности: помечайте
@MainActorтам, где это важно для thread-safety - Минимизация блокировок: не выполняйте долгие операции под
@MainActor - Использование await: при вызове асинхронных методов из
@MainActorконтекста компилятор гарантирует возврат на главный поток - Осторожность с наследованием: класс, помеченный
@MainActor, передаёт это требование всем наследникам
Правильное использование MainActor значительно уменьшает количество race conditions и crash'ей, связанных с многопоточностью, делая код более предсказуемым и устойчивым. В современных Swift-проектах это стандартный подход для обеспечения thread-safety UI-слоя.