← Назад к вопросам

Где можно отменять задачи?

2.3 Middle🔥 221 комментариев
#Многопоточность и асинхронность

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Места для отмены задач в iOS-разработке

Отмена задач (tasks) — критически важный аспект управления асинхронным кодом, ресурсами и предотвращения утечек памяти. В современной iOS-разработке есть несколько ключевых мест и механизмов для отмены.

1. В рамках Task и структуры Task

При работе с структурной конкурентностью (Structured Concurrency) в Swift можно отменять задачи через экземпляр Task или используя TaskGroup.

// Создаем задачу и сохраняем ссылку
let fetchTask = Task {
    let data = try await fetchDataFromNetwork()
    // ... обработка
}

// Где-то позже, например, в deinit или при скрытии экрана
fetchTask.cancel()

// Внутри задачи можно проверять отмену
let processingTask = Task {
    for item in largeCollection {
        // Проверяем, не отменена ли задача
        try Task.checkCancellation() // Выбросит CancellationError
        // Или проверяем вручную
        if Task.isCancelled {
            break
        }
        // ... обработка
    }
}

2. Внутри асинхронных функций

Любая асинхронная функция должна уважать отмену и периодически проверять свой статус.

func processItems(_ items: [Item]) async throws {
    for item in items {
        // Рекомендуемый способ: проверка через Task.checkCancellation()
        try Task.checkCancellation()
        
        // Альтернатива: ручная проверка
        if Task.isCancelled {
            throw CancellationError()
        }
        
        await process(item)
    }
}

3. В сочетании с Combine

При работе с фреймворком Combine отмена происходит через управление подписками (subscriptions).

class ViewModel {
    private var cancellables = Set<AnyCancellable>()
    
    func setupSubscriptions() {
        networkService
            .fetchData()
            .sink { completion in
                // ...
            } receiveValue: { data in
                // ...
            }
            .store(in: &cancellables) // Автоматическая отмена при деините
    }
    
    func manualCancel() {
        // Ручная отмена всех подписок
        cancellables.removeAll()
    }
}

4. В UIKit/UIViewController

В контроллерах представления отмена часто связана с жизненным циклом.

class MyViewController: UIViewController {
    private var fetchTask: Task<Void, Error>?
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        fetchTask = Task {
            await loadData()
        }
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        // Отменяем при скрытии экрана
        fetchTask?.cancel()
        fetchTask = nil
    }
    
    deinit {
        // Дополнительная страховка
        fetchTask?.cancel()
    }
}

5. В SwiftUI

В SwiftUI отмена тесно интегрирована с модификаторами представления и жизненным циклом.

struct ContentView: View {
    @State private var searchTask: Task<Void, Never>?
    
    var body: some View {
        SearchField(text: $query)
            .onChange(of: query) { newValue in
                // Отменяем предыдущий поиск при новом вводе
                searchTask?.cancel()
                
                searchTask = Task {
                    await performSearch(with: newValue)
                }
            }
            .onDisappear {
                // Отменяем при исчезновении представления
                searchTask?.cancel()
            }
    }
}

6. В операциях OperationQueue

Для Operation отмена происходит через метод cancel().

let operation = BlockOperation {
    // Регулярная проверка отмены
    if operation.isCancelled {
        return
    }
    // ... выполнение работы
}

// Добавляем в очередь
let queue = OperationQueue()
queue.addOperation(operation)

// Отменяем при необходимости
operation.cancel()

7. В URLSession

При работе с сетевыми запросами через URLSession отмена происходит через dataTask или Async/Await API.

// Старый подход с completion handlers
var dataTask: URLSessionDataTask?

func startRequest() {
    dataTask = URLSession.shared.dataTask(with: url) { data, response, error in
        // ...
    }
    dataTask?.resume()
}

func cancelRequest() {
    dataTask?.cancel()
    dataTask = nil
}

// Новый подход с async/await
let fetchTask = Task {
    let (data, response) = try await URLSession.shared.data(from: url)
    // ...
}

// Отмена
fetchTask.cancel()

8. В обработчиках жестов и таймерах

Для Timer, DispatchWorkItem и обработчиков жестов также требуется отмена.

// Timer
var timer: Timer?
func startTimer() {
    timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
        // ...
    }
}

func stopTimer() {
    timer?.invalidate() // Фактически отмена
    timer = nil
}

// DispatchWorkItem
var workItem: DispatchWorkItem?
func scheduleWork() {
    workItem = DispatchWorkItem {
        // ... работа
    }
    DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: workItem!)
}

func cancelWork() {
    workItem?.cancel()
    workItem = nil
}

Ключевые принципы правильной отмены:

  • Всегда проверяйте отмену внутри длительных операций
  • Освобождайте ресурсы после отмены (закрывайте сетевые соединения, файлы и т.д.)
  • Избегайте утечек памяти, сохраняя ссылки на задачи только когда они нужны
  • Уважайте иерархию отмены — отмена родительской задачи автоматически отменяет дочерние
  • Используйте defer блоки для очистки ресурсов даже при отмене
  • Тестируйте сценарии отмены для обеспечения стабильности приложения

Правильное управление отменой задач — это не только вопрос производительности, но и пользовательского опыта (быстрый отклик на действия пользователя) и эффективности использования ресурсов (батарея, память, сетевой трафик).

Где можно отменять задачи? | PrepBro