В чем разница между GCD и async/await в Swift?

Ответ

GCD (Grand Central Dispatch) — низкоуровневый C-API для управления потоками через очереди. async/await — высокоуровневая абстракция Swift Concurrency для асинхронного программирования.

Сравнительная таблица:

Аспект GCD async/await
Уровень абстракции Низкий (потоки, очереди) Высокий (задачи, продолжения)
Синтаксис Closures, callback hell Последовательный код с await
Управление потоками Ручное (DispatchQueue.main/global) Автоматическое (cooperative pool)
Отмена задач DispatchWorkItem.cancel() Task.cancel(), проверка Task.isCancelled
Обработка ошибок Через completion handlers Встроенная через throw/try/catch
Приоритеты QoS (.userInteractive, .background) TaskPriority (.high, .medium, .low)
Состояние гонки Возможно без синхронизации Предотвращается через Actors

Пример GCD:

func fetchData(completion: @escaping (Result<Data, Error>) -> Void) {
    DispatchQueue.global(qos: .userInitiated).async {
        let data = // ... загрузка
        DispatchQueue.main.async {
            completion(.success(data))
        }
    }
}

Пример async/await:

func fetchData() async throws -> Data {
    // Автоматически выполняется в background pool
    let data = try await downloadFromNetwork()
    return data
}

// Использование:
Task {
    do {
        let data = try await fetchData()
        await MainActor.run {
            updateUI(with: data) // Автоматически на главном потоке
        }
    } catch {
        handleError(error)
    }
}

Ключевые преимущества async/await:

  1. Читаемость — линейный код вместо вложенных closures
  2. Безопасность — интеграция с Actors для потокобезопасности
  3. Производительность — cooperative threads вместо создания потоков
  4. Интеграция — работа с @MainActor, TaskGroup, AsyncSequence

Когда использовать GCD:

  • Низкоуровневый контроль над потоками
  • Работа с legacy кодом
  • Таймеры (DispatchSourceTimer)
  • Барьеры для чтения/записи

Ответ 18+ 🔞

Вот смотри, блядь, сейчас мы разберёмся, что за зверь такой этот GCD и чем он от async/await отличается. Сиди, слушай, а то потом будешь в коде путаться, как мартышка в апельсинах.

GCD (Grand Central Dispatch) — это, сука, низкоуровневый C-API, который тебе самому приходится в потоках и очередях ковыряться, как в дерьме палкой. async/await — это уже высокоуровневая абстракция Swift Concurrency, где за тебя всё делает компилятор, и ты пишешь почти как синхронный код, только с магическим словечком await.

Таблица, чтобы не ебал мозг:

Аспект GCD async/await
Уровень абстракции Низкий, сам копайся в потоках Высокий, тебе почти нихуя не надо думать
Синтаксис Closures, ад коллбэков, пирамида из пизды Красивый, последовательный код с await
Управление потоками Сам решай, куда кидать (DispatchQueue.main/global) Автоматическое, умная система сама распределяет
Отмена задач DispatchWorkItem.cancel(), и молись, чтобы сработало Task.cancel(), проверь Task.isCancelled — всё чётко
Обработка ошибок Через completion handlers, сам придумывай велосипед Встроенная, через throw/try/catch, как у взрослых
Приоритеты QoS (.userInteractive, .background) TaskPriority (.high, .medium, .low)
Состояние гонки Забудь синхронизацию — получишь race condition в жопу Предотвращается через Actors, почти как в сказке

Вот как это выглядело в старые добрые времена с GCD:

func fetchData(completion: @escaping (Result<Data, Error>) -> Void) {
    DispatchQueue.global(qos: .userInitiated).async {
        let data = // ... загрузка
        DispatchQueue.main.async {
            completion(.success(data))
        }
    }
}

Видишь эту хуйню? Вложенные кложуры, переходы между очередями — один пиздец. Через три таких вызова уже не понять, где ты и куда тебя занесло.

А теперь смотри, как это делают современные ребята с async/await:

func fetchData() async throws -> Data {
    // Автоматически выполняется в background pool
    let data = try await downloadFromNetwork()
    return data
}

// Использование:
Task {
    do {
        let data = try await fetchData()
        await MainActor.run {
            updateUI(with: data) // Автоматически на главном потоке
        }
    } catch {
        handleError(error)
    }
}

Ну не красота ли, ёпта? Код читается сверху вниз, как книга. Ошибки ловятся там, где надо. И не надо вручную прыгать между очередями — система сама умная.

Основные плюсы async/await, которые тебя с ума сведут:

  1. Читаемость — линейный код вместо вложенных closures, которые мозг выедают.
  2. Безопасность — интеграция с Actors, чтобы твои данные не ебнулись от гонок.
  3. Производительность — cooperative threads вместо создания потоков на каждый чих.
  4. Интеграция — работа с @MainActor, TaskGroup, AsyncSequence — всё из коробки.

А когда всё-таки лезть в GCD?

  • Когда нужен низкоуровневый контроль над потоками, до последнего винтика.
  • Работа с легаси-кодом, который написан ещё при царе Горохе.
  • Таймеры (DispatchSourceTimer), которые надо тонко настраивать.
  • Барьеры для чтения/записи, если ты всё ещё сам это делаешь.

Короче, async/await — это будущее, блядь. Но и старый добрый GCD пока рано списывать со счетов. Главное — понимать, где что применять, а не тыкать наугад, как слепой крот.