Что такое Grand Central Dispatch (GCD) в iOS/macOS?

Ответ

Grand Central Dispatch (GCD) — это низкоуровневый фреймворк для управления параллельными и асинхронными операциями в iOS и macOS. Он абстрагирует работу с потоками, используя модель очередей (queues) и задач (в виде блоков или замыканий), и автоматически управляет пулом потоков.

Ключевые компоненты:

  • Очереди (Queues):
    • Main Queue (DispatchQueue.main): Серийная очередь, связанная с главным потоком. Используется для всех операций с UI.
    • Глобальные очереди (DispatchQueue.global(qos:)): Параллельные системные очереди с разными приоритетами качества обслуживания (Quality of Service, QoS):
      • .userInteractive — для мгновенной обратной связи (анимации, события).
      • .userInitiated — для задач, инициированных пользователем, требующих быстрого результата.
      • .utility — для длительных задач с индикатором прогресса (загрузка, вычисления).
      • .background — для задач, невидимых пользователю (синхронизация, бэкап).
    • Пользовательские очереди (DispatchQueue(label:attributes:)): Создаются разработчиком. Могут быть серийными (.serial, по умолчанию) или параллельными (.concurrent).

Основные операции:

  • async — асинхронное выполнение. Не блокирует текущий поток.
  • sync — синхронное выполнение. Блокирует текущий поток до завершения задачи.

Пример асинхронной работы с обновлением UI:

// Выполнить тяжелую задачу в фоне
DispatchQueue.global(qos: .userInitiated).async {
    let processedData = self.performHeavyCalculation()

    // Вернуться на главный поток для обновления интерфейса
    DispatchQueue.main.async {
        self.updateUI(with: processedData)
    }
}

Дополнительные механизмы GCD:

  • Группы (DispatchGroup): Для отслеживания завершения набора задач.
  • Семафоры (DispatchSemaphore): Для контроля доступа к ограниченному количеству ресурсов.
  • Барьеры (DispatchWorkItemFlags.barrier): Для обеспечения эксклюзивного доступа к ресурсу в concurrent очереди.
  • Источники (DispatchSource): Для мониторинга системных событий (таймеры, сигналы, файловые дескрипторы).

Почему GCD? Он обеспечивает эффективное использование многоядерных процессоров, упрощает многопоточное программирование и помогает избежать распространенных ошибок, таких как deadlock (при осторожном использовании sync).

Ответ 18+ 🔞

А, GCD, ёпта! Ну это ж классика, блядь! Тот самый фреймворк, который позволяет твоему приложению не висеть, как последняя мартышлюшка, когда оно что-то там грузит или считает.

Смотри, суть-то проще, чем кажется. Вместо того чтобы самому, как лох, ковыряться с потоками и думать, на какой ядро процессора отправить задачу, ты просто говоришь системе: «Э, сабака сука, э бошка думай! Вот тебе задание — выполни его где-нибудь, а мне доложи, когда готово будет».

Вот главные киты, на которых всё держится:

  • Очереди (Queues): Это как конвейеры на заводе. Задачи — это детали.
    • Главная очередь (DispatchQueue.main): Серийный, то есть однозадачный, конвейер. Связан с главным потоком. Всё, что касается интерфейса — кнопки, анимации, скроллы — всегда делается тут. Запустишь тяжёлую задачу сюда — приложение просто встанет колом, пользователь тебе в сраку чих-пых.
    • Глобальные очереди (DispatchQueue.global(qos:)): Это уже параллельные, заводские конвейеры. Система сама решает, сколько потоков под них выделить. У них есть приоритеты, чтобы система знала, что важнее:
      • .userInteractive — Срочно, блядь! Анимация или реакция на тап. Прям сейчас.
      • .userInitiated — Пользователь нажал кнопку «загрузить», ждёт быстрого результата. Тоже побыстрее.
      • .utility — Длительная хрень, типа загрузки файла или обработки фото. Можно не спешить, но прогресс-бар крутить.
      • .background — Совсем похуй, когда и как. Синхронизация данных, бэкапы. Пользователь даже не должен заметить.
    • Свои очереди: Можешь сам создать конвейер, если хочешь особый контроль. Можешь сделать его серийным (одна задача за раз) или параллельным.

Как этим пользоваться? Всего две основные команды, но они овердохуища важны:

  • async — Асинхронно. Кинул задачу в очередь и пошёл дальше, не ждёшь. Не блокирует поток. Используется в 99% случаев.
  • sync — Синхронно. Кинул задачу и стоишь, как идиот, пока она не выполнится. Текущий поток блокируется. Опасно, блядь! Можешь легко получить deadlock, особенно если на главной очереди вызвать sync. Это как самому себе встать ногой на горло.

Вот, смотри, как это выглядит в жизни, на простом примере:

// Говорим системе: "Слушай, тут у меня тяжёлая хуйня, посчитай её на каком-нибудь фоновом конвейере"
DispatchQueue.global(qos: .userInitiated).async {
    let result = self.calculateSomethingHuge() // Считаем в фоне

    // Как только посчитали, КРИЧИМ системе: "А теперь срочно на главный конвейер!"
    DispatchQueue.main.async {
        self.updateUI(with: result) // И только тут обновляем интерфейс
    }
}
// Код здесь выполнится сразу, не дожидаясь конца calculateSomethingHuge

А ещё там есть прибамбасы на все случаи жизни:

  • Группы (DispatchGroup): Чтобы знать, когда закончилась не одна, а целая орава задач. Типа «скажи мне, когда все эти 10 картинок загрузятся».
  • Семафоры (DispatchSemaphore): Чтобы ограничить доступ к чему-то. Например, «не больше 3-х сетевых запросов одновременно, а то сервер сдохнет».
  • Барьеры (DispatchWorkItemFlags.barrier): Хитрая жопа для concurrent очередей. Позволяет сказать: «все, стоп машина, пока я тут свою операцию записи не выполню, никто ничего не читает».
  • Источники (DispatchSource): Для всяких системных подписок — таймеры, наблюдение за файлами.

Зачем это всё? Да чтобы твоё приложение летало, ёпта! Чтобы интерфейс не тормозил, а процессорные ядра не простаивали, как хуй с винтом. GCD берёт всю эту гребаную магию управления потоками на себя. Главное — не накосячить с sync и не лезть с фоновыми задачами в UI. А то будет тебе не многопоточность, а один большой пиздец.