Ответ
Основные инструменты для работы с многопоточностью в экосистеме Apple:
-
Grand Central Dispatch (GCD / libdispatch): Низкоуровневый C-фреймворк для управления очередями задач.
- Очереди:
DispatchQueue(serial, concurrent),DispatchGroup,DispatchSemaphore. - Пример:
// Отправка задачи в фоновую очередь DispatchQueue.global(qos: .userInitiated).async { let result = performHeavyCalculation() // Возврат результата в главный поток для обновления UI DispatchQueue.main.async { updateUI(with: result) } }
- Очереди:
-
OperationQueue: Высокоуровневая абстракция на основе GCD. Позволяет создавать переиспользуемые
Operationс зависимостями, приоритетами и возможностью отмены.let queue = OperationQueue() queue.maxConcurrentOperationCount = 2 let downloadOp = BlockOperation { /* Загрузка данных */ } let processOp = BlockOperation { /* Обработка данных */ } processOp.addDependency(downloadOp) // Обработка начнется только после загрузки queue.addOperations([downloadOp, processOp], waitUntilFinished: false) -
Swift Concurrency (async/await): Современный, безопасный подход, представленный в Swift 5.5.
- Ключевые элементы:
async/await,Task,Actor. - Пример:
func fetchData() async throws -> Data { let (data, _) = try await URLSession.shared.data(from: url) return data }
Task { let data = try await fetchData() await MainActor.run { updateUI(with: data) } }
- Ключевые элементы:
-
Средства синхронизации: Для защиты общих ресурсов от состояний гонки.
NSLock,os_unfair_lock(более эффективный).@Atomicсвойства (через property wrappers).- Актёры (Actors) в Swift Concurrency.
Рекомендация: Для нового кода предпочтительно использовать Swift Concurrency, так как он обеспечивает безопасность на уровне компилятора и более читаемый код.
Ответ 18+ 🔞
А, ну вот, смотри, классика жанра, блядь! Многопоточность на iOS — это как цирк с конями, только лошади все сдохли, а клоуны разбежались, и ты остаёшься один на арене с этими хуёвыми шариками. Но ладно, разберёмся, что у нас тут есть.
Во-первых, старый добрый Grand Central Dispatch (GCD), он же libdispatch. Это как дед, который всё ещё может вломить, но уже путает таблетки. Низкоуровневый такой, на C, но обёрнут в красивый DispatchQueue. Суть проста, как три копейки: есть очередь, ты кидаешь в неё задачу — она выполняется. Очереди бывают серийные (одна за другой, как в очереди за водкой) и конкурентные (все лезут напролом, как в маршрутку). И главное правило, которое забывают все распиздяи: UI обновлять ТОЛЬКО с главной очереди, иначе приложение накроется медным тазом, и пользователь увидит дичь вместо интерфейса.
// Кидаем тяжёлую хуйню в фон
DispatchQueue.global(qos: .userInitiated).async {
let result = performHeavyCalculation() // тут твой код грузит ядро
// А теперь, внимание, магия: возвращаемся на главную, как барин в усадьбу
DispatchQueue.main.async {
updateUI(with: result) // и только тут тыкаем в интерфейс
}
}
Дальше идёт OperationQueue — это уже для интеллигентных ребят, которые любят планировать. Типа, ты создаёшь операции (Operation), можешь между ними зависимости выстроить (сначала скачай, потом распакуй, потом вдуй), приоритеты назначить и даже отменить всё это дело, если передумал. Удобно, когда задач дохуища и они сложные. Максимум параллелизма можно ограничить, чтобы не угробить систему.
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 2 // чтобы не распиздяйничать, только две задачи одновременно
let downloadOp = BlockOperation { /* Качаем файлы, блядь */ }
let processOp = BlockOperation { /* Обрабатываем, епта */ }
processOp.addDependency(downloadOp) // Сначала качаем, потом обрабатываем — логика, ёпта!
queue.addOperations([downloadOp, processOp], waitUntilFinished: false) // и поехали
А теперь, блядь, современность подъехала — Swift Concurrency с его async/await. Это просто песня, я тебе скажу! Всё стало красиво и безопасно, как будто тебе мамка свитер связала. Компилятор теперь сам следит, чтобы ты не накосячил с потоками. Вместо этих ёбаных колбэков и вложенных асинхронных адов — прямые, как стрела, последовательные вызовы. Выглядит, будто код синхронный, а он нихуя — асинхронный!
func fetchData() async throws -> Data {
// Никаких completionHandler, блядь! Просто ждём, пока данные придут.
let (data, _) = try await URLSession.shared.data(from: url)
return data
}
Task { // Запускаем новую таску
let data = try await fetchData() // Ждём, не дергаясь
await MainActor.run { updateUI(with: data) } // И на главный актор — обновляем UI
}
И, конечно, куда же без синхронизации, а? Потому что если несколько потоков полезют в одну переменную, будет пиздец, состояние гонки, и значения будут как у шлюхи — каждый раз разные. Раньше мучались с NSLock или os_unfair_lock, писали обёртки @Atomic. А теперь, ёпта, есть акторы (Actors) в Swift Concurrency! Это такие изолированные хуйни, которые сами следят, чтобы к их данным был доступ только из одного потока за раз. Красота, блядь!
Итог, Колян: Если пишешь новый проект — даже не думай, блядь, бери Swift Concurrency. Это как перейти с каменного топора на бензопилу. Код чище, безопаснее, и голова болит меньше. А старые GCD и OperationQueue оставь для легаси-кода или очень специфичных случаев, когда надо по-старинке, с чувством, с толком, с расстановкой.