Какие основные инструменты и модели многопоточности доступны в Swift?

Ответ

Swift предлагает несколько уровней абстракции для работы с многопоточностью, от низкоуровневых системных API до современных языковых конструкций.

1. Grand Central Dispatch (GCD) Низкоуровневый C-API, управляющий очередями задач (DispatchQueue).

  • Главная очередь (Main Queue): DispatchQueue.main — для обновления UI.
  • Глобальные очереди: Фоновые очереди с приоритетами качества обслуживания (QoS).
    DispatchQueue.global(qos: .userInitiated).async {
        // Тяжёлая задача
        let result = processData()
        // Возврат в главный поток для обновления UI
        DispatchQueue.main.async {
            updateUI(with: result)
        }
    }
  • Пользовательские очереди: Можно создавать серийные (.serial) или конкурентные (.concurrent) очереди.

2. OperationQueue Высокоуровневая Objective-C-абстракция над GCD, построенная на классах Operation. Позволяет:

  • Устанавливать зависимости между задачами.
  • Отменять задачи.
  • Ограничивать количество параллельных операций (maxConcurrentOperationCount).

3. Swift Concurrency (async/await) Современная модель, представленная в Swift 5.5. Упрощает написание асинхронного и параллельного кода.

  • Асинхронные функции: Помечаются ключевым словом async.
    func fetchData() async throws -> Data {
        let (data, _) = try await URLSession.shared.data(from: url)
        return data
    }
  • Задачи (Task): Являются единицей выполнения.
    Task {
        // Автоматически выполняется в фоновом потоке
        let data = await fetchData()
        // `await MainActor.run` гарантирует выполнение в главном потоке
        await MainActor.run {
            self.label.text = String(data: data, encoding: .utf8)
        }
    }
  • Акторы (Actors): Предоставляют механизм изоляции состояния для безопасного доступа из разных потоков.

Выбор инструмента:

  • Swift Concurrency — предпочтительный выбор для нового кода.
  • OperationQueue — для сложных зависимостей и отменяемых задач.
  • GCD — для простых фоновых задач или низкоуровневого контроля.

Ответ 18+ 🔞

Слушай, а ведь Swift, этот хитрожопый язык, на самом деле даёт нам целую палитру способов устроить многопоточный бардак. От таких низкоуровневых штук, что прям в ядро системы лезут, до этих ваших модных async/await, от которых у старых разработчиков волосы дыбом встают. Давай разбираться, пока не съехала крыша.

1. Grand Central Dispatch (GCD) — старый, добрый, местами злой. Это как бы такой фундаментальный C-API, который вертит очередями задач, будто шаманы бубнами. Основа основ — DispatchQueue.

  • Главная очередь (Main Queue): DispatchQueue.main. Это святое, блядь. Сюда только обновлять интерфейс. Кто сюда лезет с тяжёлой задачей — тот полупидор.
  • Глобальные очереди: Фоновые рабочие лошадки. У них даже приоритеты есть, QoS называется. .userInitiated — «срочно, ёпта», .utility — «сделай как-нибудь потом».
    // Кидаем тяжёлую хуйню в фон
    DispatchQueue.global(qos: .userInitiated).async {
        let result = self.processData() // Тут всё грузится и тормозит
        // А теперь ВОЗВРАЩАЕМСЯ НА ГЛАВНУЮ, сука, не забываем!
        DispatchQueue.main.async {
            self.updateUI(with: result) // И только тут тыкаем в интерфейс
        }
    }
  • Свои очереди: Можешь наклепать серийных (.serial, одна задача за другой) или конкурентных (.concurrent, несколько сразу). Как тебе угодно, главное — не перепутай, а то получишь гонку данных, и пиши пропало.

2. OperationQueue — хитрая надстройка над GCD. Высокоуровневая абстракция из мира Objective-C. Работает с объектами Operation. Её фишка в том, что она не просто выполняет, а управляет задачами. Можно сказать одной операции: «Э, дружок, не начинай, пока вот эта вот не закончит» (зависимости). Или взять и отменить всё к хуям собачьим. Или ограничить, сколько операций могут городить огород одновременно (maxConcurrentOperationCount). Удобно для сложных пасьянсов.

3. Swift Concurrency (async/await) — новое, модное, молодёжное. Появилось в Swift 5.5 и всех поначалу оху... то есть, сильно удивило. Суть — писать асинхронный код почти как обычный, линейный.

  • Асинхронные функции: Помечаешь async и спишь спокойно.
    func fetchData() async throws -> Data {
        // Смотри, какая красота! Никаких completionHandler-ов, одна строка!
        let (data, _) = try await URLSession.shared.data(from: url)
        return data
    }
  • Задачи (Task): Это такая обёртка, внутри которой этот волшебный async-код и выполняется.
    Task {
        // Всё, что после `await`, автоматически уедет в фон, если надо
        let data = try? await fetchData()
        // А чтобы ткнуть в UI, надо явно сказать: «Эй, я на главный поток хочу!»
        await MainActor.run {
            self.label.text = String(data: data, encoding: .utf8)
        }
    }
  • Акторы (Actors): Это вообще песня. Такая сущность, которая защищает свои внутренние данные от пиздеца, когда к ним ломятся со всех потоков сразу. Самый надёжный буфер от твоей криворукости.

Так что же выбрать, ёпта?

  • Swift Concurrency — это сейчас главная дорога. Для всего нового кода — бери это. Прям вот в рот меня чих-пых, как же удобно!
  • OperationQueue — если у тебя там задачи, как в матрёшке, одна из другой вылезает, или тебе жизненно важно иметь кнопку «Отмена».
  • GCD — для простых одноразовых выстрелов в фон («сохрани файлик») или если ты любитель поковыряться в низкоуровневых кишках. Старая, добрая, проверенная временем кувалда.