← Назад к вопросам

Что следует помечать MainActor?

1.7 Middle🔥 211 комментариев
#Многопоточность и асинхронность

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что следует помечать 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

  1. Фоновые операции (сетевая загрузка, обработка изображений, вычисления)
  2. Потокобезопасные структуры данных, не связанные с UI
  3. Чисто бизнес-логические модули без UI-зависимостей
  4. Низкоуровневые утилиты (парсеры, кодировщики)

Практический пример архитектуры с 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-слоя.

Что следует помечать MainActor? | PrepBro