OperationQueue to wysokopoziomowa kolejka zadań w iOS i macOS, zbudowana na bazie Grand Central Dispatch. Według Apple Developer Documentation, 2026, OperationQueue zarządza instancjami Operation — obiektami enkapsulującymi jednostkę pracy. W przeciwieństwie do DispatchQueue, OperationQueue obsługuje zależności między operacjami, priorytety, KVO i anulowanie uruchomionych zadań. OperationQueue automatycznie zarządza pulą wątków, rozdzielając operacje według dostępnych zasobów systemu.
Najważniejsze
OperationQueue to klasa z frameworka Foundation zarządzająca wykonywaniem obiektów Operation. W przeciwieństwie do DispatchQueue, OperationQueue nie wymaga jawnego określania trybu serial lub concurrent — liczba jednocześnie wykonywanych operacji jest regulowana właściwością maxConcurrentOperationCount. Wartość 1 zamienia kolejkę w sequential, każda inna — w concurrent.
Operation to abstrakcyjna klasa reprezentująca jednostkę pracy. Każda operacja ma stan: ready, executing, finished lub cancelled. Stany są zgodne z KVO (Key-Value Observing), co pozwala reagować na zmiany — na przykład aktualizować UI po zakończeniu operacji. Operation automatycznie zarządza flagami isExecuting i isFinished.
Według Apple (2026), OperationQueue używa GCD pod maską, ale dodaje funkcjonalności niedostępne w DispatchQueue: zależności, priorytety i anulowanie operacji. Jeśli aplikacja przechodzi w tryb tła, OperationQueue wstrzymuje wykonywanie, a po powrocie — wznawia. OperationQueue również automatycznie uwzględnia liczbę rdzeni procesora i wybiera optymalną liczbę wątków.
Każda operacja przechodzi przez cztery stany: pending (oczekiwanie), ready (gotowa do uruchomienia), executing (wykonywana) i finished (zakończona). Stan cancelled może nastąpić na każdym etapie przed zakończeniem. Przejścia między stanami są śledzone przez KVO — to podstawa reaktywnego aktualizowania UI. OperationQueue automatycznie usuwa zakończone operacje z kolejki i powiadamia zależne operacje, że ich warunek wstępny został spełniony, uruchamiając ich wykonanie.
Operation to abstrakcyjna klasa wymagająca nadpisania metody main() lub start(). W metodzie main() umieszcza się kod zadania, a stan isExecuting i isFinished są zarządzane automatycznie. Dla operacji asynchronicznych wymagane jest nadpisanie start() i ręczne zarządzanie flagami stanu.
BlockOperation to konkretna implementacja Operation wykonująca jeden lub więcej bloków kodu. BlockOperation staje się concurrent, jeśli dodać do niej kilka bloków przez addExecutionBlock(). Operacja kończy się dopiero po wykonaniu wszystkich dodanych bloków. BlockOperation to wygodna alternatywa dla prostych zadań bez dziedziczenia.
| Cechy | Operation | BlockOperation |
|---|---|---|
| Typ klasy | Abstrakcyjna | Konkretna |
| Dziedziczenie | Wymagane | Niewymagane |
| Asynchroniczność | Ręczne zarządzanie KVO | Automatyczne |
| Bloków kodu | Jeden w main() | Jeden lub więcej |
| Zastosowanie | Złożone zadania ze stanem | Proste jednorazowe zadania |
| Nadaje się do | Zależności, anulowanie, postęp | Szybkie bloki, completion |
| Pamięć | Większa przez KVO i stan | Minimalna, lekka |
Aby utworzyć niestandardową operację, dziedzicz po Operation i nadpisz main(). Wewnątrz sprawdzaj flagę isCancelled przed kosztownymi operacjami, aby zapewnić szybkie anulowanie. Jest to krytyczne przy pobieraniu dużych plików lub wsadowym przetwarzaniu danych. Wybór między Operation a BlockOperation zależy od złożoności zadania: dla prostych jednorazowych działań wystarczy BlockOperation, dla wielokrotnie używanej logiki ze stanem — dziedziczenie po Operation.
Zależności to kluczowa zaleta OperationQueue nad DispatchQueue. Metoda addDependency(_:) określa, że operacja B wykonuje się dopiero po zakończeniu operacji A. Zależności tworzą directed acyclic graph (DAG): po dodaniu zależności cyklicznej kolejka ją ignoruje, a operacje nie są uruchamiane.
Priorytet operacji jest ustawiany właściwością queuePriority z wartościami: .veryLow, .low, .normal, .high, .veryHigh. Priorytet wpływa na kolejność uruchamiania wśród ready-operacji, ale nie zastępuje zależności. OperationQueue najpierw uwzględnia zależności, potem — priorytet wśród dostępnych operacji.
Typowy scenariusz — pobieranie danych profilu: najpierw pobieramy użytkownika, następnie na podstawie jego id pobieramy znajomych i posty. Ustawienie zależności między pobieraniem użytkownika a pobieraniem znajomych gwarantuje prawidłową kolejność bez zagnieżdżonych completion handler.
Właściwość maxConcurrentOperationCount ogranicza liczbę jednocześnie wykonywanych operacji. Wartość 1 tworzy kolejkę sequential, wartość domyślna (NSOperationQueueDefaultMaxConcurrentOperationCount) — optymalna systemowo, zależna od bieżącego obciążenia urządzenia. Prawidłowe ustawienie tego parametru zapobiega nadmiernemu zużyciu zasobów: do pobierania obrazów wystarczy 4-6 concurrent operacji, do zadań CPU-intensywnych — liczba rdzeni procesora.
Wybór między OperationQueue a DispatchQueue zależy od złożoności zadania. DispatchQueue to lekkie narzędzie do prostych wywołań async. OperationQueue to cięższe rozwiązanie dla złożonych scenariuszy z wieloma powiązanymi zadaniami. Apple zaleca zaczynać od DispatchQueue i przechodzić na OperationQueue tylko w razie potrzeby zależności lub anulowania. Dla większości projektów iOS kombinacja obu narzędzi daje optymalny balans wydajności i elastyczności.
Według Ray Wenderlich (2025), w dużych projektach iOS OperationQueue jest używana do pobierania treści z postępem i anulowaniem, a DispatchQueue — do wszystkich pozostałych operacji async. Stosunek wynosi około 20 do 80 na korzyść DispatchQueue.
Omówimy trzy przykłady: prosty BlockOperation, niestandardową Operation z zależnościami i anulowalną operację do pobierania danych.
Najprostszy przypadek — wykonaj blok na OperationQueue i przetwórz wynik przez completionBlock. Każda Operation ma wbudowaną właściwość completionBlock, wywoływaną po zakończeniu main().
let queue = OperationQueue()
let operation = BlockOperation()
operation.addExecutionBlock {
let data = NetworkService.fetchData()
OperationQueue.main.addOperation {
self.updateUI(data)
}
}
queue.addOperation(operation)
Zależność gwarantuje, że parseOperation uruchomi się dopiero po zakończeniu downloadOperation. Eliminuje to potrzebę zagnieżdżonych callback.
let download = BlockOperation { self.downloadJSON() }
let parse = BlockOperation { self.parseJSON() }
parse.addDependency(download)
let queue = OperationQueue()
queue.addOperations([download, parse], waitUntilFinished: false)
Nadpisz main() z okresowym sprawdzaniem isCancelled. Pozwala to natychmiast zatrzymać operację przy anulowaniu, bez czekania na zakończenie kosztownej operacji.
class ImageLoadOperation: Operation {
override func main() {
guard !self.isCancelled else { return }
let image = self.downloadImage()
guard !self.isCancelled else { return }
OperationQueue.main.addOperation { self.display(image) }
}
}
Anulowanie operacji ustawia flagę isCancelled na true, ale nie zatrzymuje już uruchomionej metody main(). Kod wewnątrz main() musi samodzielnie sprawdzać isCancelled i kończyć działanie w razie potrzeby. To architektoniczna decyzja Apple — pozwalająca programiście prawidłowo zwolnić zasoby przy anulowaniu.
KVO-observacja właściwości isFinished i isExecuting pozwala reagować na zakończenie operacji bez explicit callback. OperationQueue automatycznie usuwa zakończone operacje z kolejki, ale pozostają one w pamięci, dopóki istnieją silne referencje. KVO to podstawa integracji OperationQueue z reaktywnymi frameworkami typu RxSwift lub Combine.
Subskrypcja isCancelled przez KVO pozwala aktualizować UI przy anulowaniu operacji — na przykład wyświetlać placeholder zamiast anulowanego pobierania. Właściwość isCancelled jest KVO-zgodna, co czyni ją wygodną dla reaktywnych pipeline.
Nie twórz operacji w dużych ilościach — każda Operation jest osobnym obiektem w pamięci. Jeśli zadanie jest krótkie i nie wymaga zależności, używaj DispatchQueue bezpośrednio. OperationQueue jest uzasadniona dla złożonych scenariuszy z jawnymi zależnościami, anulowaniem i monitorowaniem postępu.
Sprawdzaj isCancelled przed kosztownymi operacjami wewnątrz metody main(). W przypadku pobierania plików lub przetwarzania obrazów sprawdzanie po każdym znaczącym kroku zapewnia szybką odpowiedź na anulowanie. Używaj if isCancelled { return } na początku main() i po każdej dużej operacji.
Prawidłowo zarządzaj completionBlock. Właściwość completionBlock operacji jest wywoływana po zakończeniu main(), nawet jeśli operacja została anulowana. Sprawdzaj isCancelled wewnątrz completionBlock, aby nie aktualizować UI błędnymi danymi. OperationQueue.main — bezpieczna wątkowo kolejka dla operacji UI, analogiczna do DispatchQueue.main.
Unikaj zależności cyklicznych — prowadzą one do tego, że żadna z operacji w cyklu nigdy się nie uruchomi. OperationQueue nie wykrywa cykli automatycznie: jeśli A zależy od B, a B zależy od A, obie na zawsze pozostaną w stanie ready. Planuj graf zależności z wyprzedzeniem.
Często zadawane pytania
OperationQueue jest zbudowana na GCD i dodaje zależności, priorytety, KVO i anulowanie operacji. DispatchQueue to lżejsze narzędzie do prostych zadań async bez tych możliwości.
Ustaw właściwość maxConcurrentOperationCount na 1. To zamienia OperationQueue w kolejkę sequential z zachowaniem wszystkich zalet — zależności, priorytetów i anulowania.
Metoda cancel() ustawia flagę isCancelled, ale nie zatrzymuje wykonującej się metody main(). Kod operacji musi sam sprawdzać isCancelled i kończyć działanie. Anulowanie działa tylko dla pending i ready operacji.
Operację warto dziedziczyć, gdy wymagane jest zarządzanie stanem, asynchroniczność lub ponowne użycie logiki. BlockOperation nadaje się do prostych jednorazowych zadań bez dziedziczenia.
Nie, chyba że wywołano metodę waitUntilFinished z parametrem true na głównym wątku. Operacje domyślnie wykonują się na wątkach tła, a wynik jest zwracany przez OperationQueue.main.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również