Какие механизмы для отмены асинхронных задач существуют в iOS?

«Какие механизмы для отмены асинхронных задач существуют в iOS?» — вопрос из категории Многопоточность, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Отмена задач важна для управления ресурсами и отзывчивости UI. Механизмы различаются в зависимости от используемого API.

1. Grand Central Dispatch (GCD) - DispatchWorkItem

Задачу, представленную DispatchWorkItem, можно отменить до ее начала. Если выполнение уже началось, его нельзя прервать, но можно проверить флаг isCancelled внутри блока.

let workItem = DispatchWorkItem {
    // Регулярно проверяем отмену
    while !Thread.current.isCancelled {
        // Долгая операция
    }
}
DispatchQueue.global().async(execute: workItem)
// Отмена (остановит задачу, если она еще не началась)
workItem.cancel()

2. Operation и OperationQueue

Operation предоставляет более богатый API для отмены и зависимостей.

let operation = BlockOperation {
    guard !operation.isCancelled else { return }
    // Выполняем работу
}
let queue = OperationQueue()
queue.addOperation(operation)
// Отмена. Задача проверит isCancelled и завершится корректно.
operation.cancel()

3. URLSession

Любую сетевую задачу (URLSessionDataTask, URLSessionDownloadTask) можно отменить.

let task = URLSession.shared.dataTask(with: url) { data, _, _ in }
task.resume()
// Отмена. Вызовет completionHandler с ошибкой URLError.cancelled.
task.cancel()

4. Combine

Подписка возвращает AnyCancellable, вызов cancel() на котором прекращает получение значений.

var cancellables = Set<AnyCancellable>()

URLSession.shared.dataTaskPublisher(for: url)
    .sink(receiveCompletion: { _ in }, receiveValue: { _ in })
    .store(in: &cancellables) // Храним ссылку

// Отмена всех подписок в наборе
cancellables.forEach { $0.cancel() }

5. Swift Concurrency (async/await)

Задача, созданная с помощью Task, может быть отменена. Внутри асинхронной функции следует проверять Task.isCancelled или вызывать try Task.checkCancellation() (которая выбросит CancellationError).

let task = Task {
    // Проверка 1: Выброс ошибки
    try Task.checkCancellation()

    // Проверка 2: Условный выход
    guard !Task.isCancelled else { return }

    // Долгая работа
}

// Отмена задачи
task.cancel()

Ключевой принцип: Отмена обычно кооперативна. API предоставляет флаг или уведомление, но сама задача должна регулярно проверять его и корректно завершать работу.