Что такое DispatchWorkItem и для чего он используется?

Ответ

DispatchWorkItem — это объектная обертка над замыканием (closure) в GCD, предоставляющая расширенный контроль над выполнением задачи: отмену, задержку, уведомление о завершении.

Ключевые возможности:

  • Отмена задачи: Метод cancel(). Отмененная задача не будет запущена, а если уже выполняется, может проверить свойство isCancelled.
  • Задержка выполнения: Используется с DispatchQueue.asyncAfter.
  • Синхронный запуск: Метод perform() для немедленного выполнения в текущем потоке.
  • Уведомления: Можно установить обработчик завершения через notify(queue:execute:).

Примеры использования:

1. Отмена отложенной задачи (поиск с debounce):

class SearchManager {
    private var pendingSearchWorkItem: DispatchWorkItem?

    func performSearch(query: String) {
        // Отменяем предыдущий отложенный поиск
        pendingSearchWorkItem?.cancel()

        let newWorkItem = DispatchWorkItem { [weak self] in
            self?.executeSearch(query: query)
        }
        pendingSearchWorkItem = newWorkItem

        // Запускаем новый поиск через 0.3 секунды
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: newWorkItem)
    }
}

2. Зависимости и уведомления:

let backgroundWorkItem = DispatchWorkItem { /* Тяжелая задача */ }
let completionWorkItem = DispatchWorkItem { print("Завершено") }

// Запустить completionWorkItem после backgroundWorkItem
backgroundWorkItem.notify(queue: .main, execute: completionWorkItem)

DispatchQueue.global().async(execute: backgroundWorkItem)

Преимущество перед простым замыканием: DispatchWorkItem инкапсулирует состояние и логику управления жизненным циклом задачи.

Ответ 18+ 🔞

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

DispatchWorkItem — это, по сути, твоё обычное замыкание, но в бронежилете и с пультом управления, ёпта. Взяли closure, завернули в объект, и теперь можно им вертеть как хочешь: отменять, откладывать, ставить в очередь на выполнение после другой задачи. Вообще, красота, а не хуйня.

Что эта бандура умеет, блядь:

  • Отменить задачу нахуй: Есть метод cancel(). Если задача ещё не стартанула — она и не стартанет. А если уже в процессе, то внутри неё можно проверить isCancelled и аккуратно свернуть лавочку.
  • Задержать выполнение: Классика — DispatchQueue.asyncAfter. Отправил задачу в будущее, как письмо с опозданием.
  • Запустить тут же, синхронно: Метод perform() — хрясь, и выполняется прямо в текущем потоке, не отходя от кассы.
  • Сообщать о своей кончине: Через notify(queue:execute:) можно прицепить другую задачку, которая запустится, когда первая благополучно сдохнет.

Смотри, как это в деле выглядит, на двух пальцах:

1. Умный поиск, который не дрочит сервер каждое нажатие клавиши (debounce):

class SearchManager {
    private var pendingSearchWorkItem: DispatchWorkItem?

    func performSearch(query: String) {
        // А ну-ка отмена, старая задача! Хуй тебе, а не поиск.
        pendingSearchWorkItem?.cancel()

        let newWorkItem = DispatchWorkItem { [weak self] in
            self?.executeSearch(query: query)
        }
        pendingSearchWorkItem = newWorkItem

        // А новую запустим через 0.3 секунды, если юзер уже успокоился.
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: newWorkItem)
    }
}

Вот и всё, волнение ебать! Раньше юзер вводил "к", "ко", "кот" — и три запроса летело. А теперь только один, последний. Умно, да? Не то что некоторые.

2. Задачи-паразиты, которые ждут друг друга (уведомления):

let backgroundWorkItem = DispatchWorkItem { /* Какая-нибудь тяжёлая хуйня, типа обработки фото */ }
let completionWorkItem = DispatchWorkItem { print("Всё, готово, можно пить чай") }

// Говорим completionWorkItem: "Слушай сюда, дружок. Ты стартуешь только после того, как backgroundWorkItem закончит свою волынку".
backgroundWorkItem.notify(queue: .main, execute: completionWorkItem)

// И отправляем тяжёлую задачу в бэкграунд.
DispatchQueue.global().async(execute: backgroundWorkItem)

И понеслась! Первая задача пашет, вторая терпеливо ждёт на главной очереди своего звёздного часа. Красота!

Так в чём же, блядь, соль? А соль в том, что простое замыкание — это как голый мужик на морозе. А DispatchWorkItem — это тот же мужик, но в тёплой куртке, с документами и навигатором в кармане. Он инкапсулирует в себе всю логику управления: состояние, отмену, зависимости. Не нужно городить костыли из флагов и таймеров — взял готовый инструмент и поехал. Пизда с ушами, а не решение!