Ответ
Отмена задач важна для управления ресурсами и отзывчивости 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 предоставляет флаг или уведомление, но сама задача должна регулярно проверять его и корректно завершать работу.
Ответ 18+ 🔞
А, слушай, про отмену задач! Это ж святое, блядь. Ну представь: запустил ты какую-нибудь хуйню на фон, а пользователь уже в другую вкладку смылся. Ресурсы жрутся, батарея садится — пиздец, а не разработка. Надо уметь всё это хозяйство прибивать, как назойливую муху.
Вот смотри, как это в разных угодьях делается, блядь.
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 — она умнее, с характером. У неё прямо свой флаг isCancelled есть, который она сама проверяет в нужных местах. Красота!
let operation = BlockOperation {
// Первым делом глянь — а не отменили ли нас уже?
guard !operation.isCancelled else { return }
// Если нет — работаем, блядь
}
let queue = OperationQueue()
queue.addOperation(operation)
// Раз! И отмена. Операция сама увидит флаг и вежливо съебёт.
operation.cancel()
Тут уже кооперация получше, но опять же — если внутри операции цикл бесконечный и она про флаг не знает, то будет работать, пока приложение не сдохнет. Сам дурак.
3. URLSession
С сетью всё просто, как три копейки, ёпта. Запустил таску — получил URLSessionDataTask. Захотел отменить — дерни cancel(). CompletionHandler тебе сразу прилетит с ошибкой URLError.cancelled. Чисто, аккуратно.
let task = URLSession.shared.dataTask(with: url) { data, _, _ in }
task.resume()
// Ой, передумал качать эту гигабайтную порнуху!
task.cancel()
Вот это я понимаю — цивилизация, блядь. Не надо никаких флагов вручную проверять.
4. Combine
А, этот франт в цилиндре! Тут подписка — она как AnyCancellable. Пока ты её хранишь — стрим течёт. Вызвал cancel() — всё, кранты, поток перекрыт. Обычно их в Set пихают, чтобы потом всех разом порешать.
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.cancel(). А внутри задачи уже сам решай, как на это реагировать. Можно через try Task.checkCancellation() — она тебе CancellationError в рожу кинет. Можно флаг Task.isCancelled пощупать.
let task = Task {
// Проверка первая, агрессивная: кинет ошибку, если отмена
try Task.checkCancellation()
// Проверка вторая, тактичная: просто выйдем тихонько
guard !Task.isCancelled else { return }
// Ну а если всё ок — гоним лошадей
}
// Внезапно передумали! Отмена!
task.cancel()
И главная мысль, которую ты должен вынести, блядь: Отмена — она почти всегда кооперативная, сука. Система тебе вежливо говорит: «Знаешь, дружок, может, уже хватит?». А ты уже сам решай — или послушаться и аккуратно завершиться, или быть мудаком и дальше впустую ресурсы жечь. Умный разработчик всегда слушается, ёпта.