OperationQueue è una coda di attività di alto livello in iOS e macOS costruita sopra Grand Central Dispatch. Secondo Apple Developer Documentation, 2026, OperationQueue gestisce istanze di Operation — oggetti che incapsulano un'unità di lavoro. A differenza di DispatchQueue, OperationQueue supporta dipendenze tra operazioni, priorità, osservazione KVO e cancellazione di attività in esecuzione. OperationQueue gestisce automaticamente un pool di thread, distribuendo le operazioni tra le risorse di sistema disponibili.
Punti Chiave
OperationQueue è una classe del framework Foundation che gestisce l'esecuzione di oggetti Operation. A differenza di DispatchQueue, OperationQueue non richiede di specificare esplicitamente la modalità seriale o concorrente — il numero di operazioni eseguite simultaneamente è controllato dalla proprietà maxConcurrentOperationCount. Il valore 1 rende la coda sequenziale, qualsiasi altro valore la rende concorrente.
Operation è una classe astratta che rappresenta un'unità di lavoro. Ogni operazione ha uno stato: ready, executing, finished o cancelled. Gli stati sono compatibili con KVO (Key-Value Observing), consentendo di reagire ai cambiamenti — ad esempio, aggiornare l'interfaccia utente al completamento di un'operazione. Operation gestisce automaticamente i flag isExecuting e isFinished.
Secondo Apple (2026), OperationQueue utilizza GCD internamente ma aggiunge funzionalità non disponibili in DispatchQueue: dipendenze, priorità e cancellazione delle operazioni. Se l'app entra in background, OperationQueue sospende l'esecuzione e la riprende al ritorno. OperationQueue considera automaticamente anche il numero di core della CPU e seleziona il numero ottimale di thread.
Ogni operazione attraversa quattro stati: pending (in attesa), ready (pronta), executing (in esecuzione) e finished (completata). Lo stato cancelled può verificarsi in qualsiasi fase prima del completamento. Le transizioni di stato vengono tracciate tramite KVO — questa è la base per gli aggiornamenti reattivi dell'interfaccia. OperationQueue rimuove automaticamente le operazioni completate dalla coda e notifica alle operazioni dipendenti che il loro prerequisito è stato soddisfatto, attivandone l'esecuzione.
Operation è una classe astratta che richiede la sovrascrittura del metodo main() o start(). Il codice dell'attività viene inserito nel metodo main(), e gli stati isExecuting e isFinished vengono gestiti automaticamente. Per le operazioni asincrone, è necessario sovrascrivere start() e gestire manualmente i flag di stato.
BlockOperation è un'implementazione concreta di Operation che esegue uno o più blocchi di codice. BlockOperation diventa concorrente se si aggiungono più blocchi tramite addExecutionBlock(). L'operazione viene completata solo dopo l'esecuzione di tutti i blocchi aggiunti. BlockOperation è un'alternativa conveniente per attività semplici senza ereditarietà.
| Caratteristica | Operation | BlockOperation |
|---|---|---|
| Tipo di Classe | Astratta | Concreta |
| Ereditarietà | Richiesta | Non richiesta |
| Asincronia | Gestione manuale KVO | Automatica |
| Blocchi di Codice | Uno in main() | Uno o più |
| Utilizzo | Attività complesse con stato | Attività semplici una tantum |
| Adatto per | Dipendenze, cancellazione, progresso | Blocchi rapidi, completamento |
| Memoria | Più alta a causa di KVO e stato | Minima, leggera |
Per creare un'operazione personalizzata, ereditare da Operation e sovrascrivere main(). All'interno, verificare il flag isCancelled prima di operazioni costose per garantire una cancellazione rapida. Questo è critico per il download di file di grandi dimensioni o l'elaborazione batch di dati. La scelta tra Operation e BlockOperation dipende dalla complessità dell'attività: per azioni semplici una tantum, BlockOperation è sufficiente; per logica riutilizzabile con stato, ereditare da Operation.
Le dipendenze sono il vantaggio principale di OperationQueue rispetto a DispatchQueue. Il metodo addDependency(_:) specifica che l'operazione B viene eseguita solo dopo il completamento dell'operazione A. Le dipendenze formano un grafo diretto aciclico (DAG): se viene aggiunta una dipendenza ciclica, la coda la ignora e le operazioni non vengono avviate.
La priorità di un'operazione viene impostata tramite la proprietà queuePriority con valori: .veryLow, .low, .normal, .high, .veryHigh. La priorità influenza l'ordine di avvio tra le operazioni pronte ma non sostituisce le dipendenze. OperationQueue prima risolve le dipendenze, poi applica la priorità tra le operazioni disponibili.
Uno scenario tipico — caricamento dei dati del profilo: prima caricare l'utente, poi in base al suo id caricare amici e post. Impostare una dipendenza tra caricare l'utente e caricare gli amici garantisce l'ordine corretto senza gestori di completamento annidati.
La proprietà maxConcurrentOperationCount limita il numero di operazioni eseguite simultaneamente. Il valore 1 crea una coda sequenziale, il valore predefinito (NSOperationQueueDefaultMaxConcurrentOperationCount) è ottimale per il sistema e dipende dal carico corrente del dispositivo. La configurazione corretta di questo parametro previene il consumo eccessivo di risorse: per il caricamento di immagini, 4–6 operazioni concorrenti sono sufficienti; per attività intensive di CPU, utilizzare il numero di core del processore.
La scelta tra OperationQueue e DispatchQueue dipende dalla complessità dell'attività. DispatchQueue è uno strumento leggero per semplici chiamate asincrone. OperationQueue è una soluzione più pesante per scenari complessi con molte attività interconnesse. Apple consiglia di iniziare con DispatchQueue e passare a OperationQueue solo quando sono necessarie dipendenze o cancellazione. Per la maggior parte dei progetti iOS, una combinazione di entrambi gli strumenti offre un equilibrio ottimale tra prestazioni e flessibilità.
Secondo Ray Wenderlich (2025), nei grandi progetti iOS, OperationQueue viene utilizzato per caricare contenuti con progresso e cancellazione, mentre DispatchQueue viene utilizzato per tutte le altre operazioni asincrone. Il rapporto è di circa 20 a 80 a favore di DispatchQueue.
Vediamo tre esempi: un semplice BlockOperation, una Operation personalizzata con dipendenze e un'operazione annullabile per il caricamento dei dati.
Il caso più semplice — eseguire un blocco su OperationQueue e gestire il risultato tramite completionBlock. Ogni Operation ha una proprietà completionBlock incorporata, chiamata dopo la fine di main().
let queue = OperationQueue()
let operation = BlockOperation()
operation.addExecutionBlock {
let data = NetworkService.fetchData()
OperationQueue.main.addOperation {
self.updateUI(data)
}
}
queue.addOperation(operation)
La dipendenza garantisce che parseOperation venga avviata solo dopo il completamento di downloadOperation. Ciò elimina la necessità di callback annidati.
let download = BlockOperation { self.downloadJSON() }
let parse = BlockOperation { self.parseJSON() }
parse.addDependency(download)
let queue = OperationQueue()
queue.addOperations([download, parse], waitUntilFinished: false)
Sovrascrivere main() con controlli periodici di isCancelled. Ciò consente all'operazione di fermarsi immediatamente al momento dell'annullamento, senza attendere il completamento di un'operazione costosa.
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) }
}
}
La cancellazione di un'operazione imposta il flag isCancelled su true, ma non ferma il metodo main() già in esecuzione. Il codice all'interno di main() deve verificare isCancelled da solo e terminare quando necessario. Questa è una decisione architetturale di Apple — che consente allo sviluppatore di rilasciare correttamente le risorse in caso di annullamento.
L'osservazione KVO delle proprietà isFinished e isExecuting consente di reagire al completamento delle operazioni senza callback espliciti. OperationQueue rimuove automaticamente le operazioni completate dalla coda, ma rimangono in memoria finché ci sono riferimenti forti ad esse. KVO è la base per integrare OperationQueue con framework reattivi come RxSwift o Combine.
Sottoscriversi a isCancelled tramite KVO consente di aggiornare l'interfaccia utente al momento dell'annullamento di un'operazione — ad esempio, mostrare un segnaposto invece di un download annullato. La proprietà isCancelled è compatibile con KVO, rendendola conveniente per pipeline reattive.
Non creare grandi quantità di operazioni — ogni Operation è un oggetto separato in memoria. Se un'attività è breve e non richiede dipendenze, utilizzare DispatchQueue direttamente. OperationQueue è giustificata per scenari complessi con dipendenze esplicite, cancellazione e monitoraggio del progresso.
Verificare isCancelled prima di operazioni costose all'interno del metodo main(). In caso di download di file o elaborazione di immagini, verificare dopo ogni passaggio significativo garantisce una risposta rapida all'annullamento. Utilizzare if isCancelled { return } all'inizio di main() e dopo ogni operazione importante.
Gestire completionBlock correttamente. La proprietà completionBlock di un'operazione viene chiamata dopo la fine di main(), anche se l'operazione è stata annullata. Verificare isCancelled all'interno di completionBlock per evitare di aggiornare l'interfaccia con dati errati. OperationQueue.main è una coda thread-safe per le operazioni dell'interfaccia, simile a DispatchQueue.main.
Evitare dipendenze cicliche — impediscono l'avvio di qualsiasi operazione nel ciclo. OperationQueue non rileva automaticamente i cicli: se A dipende da B e B dipende da A, entrambe rimarranno per sempre nello stato ready. Pianificare il grafo delle dipendenze in anticipo.
Domande Frequenti
OperationQueue è costruito su GCD e aggiunge dipendenze, priorità, KVO e cancellazione delle operazioni. DispatchQueue è uno strumento più leggero per attività asincrone semplici senza queste capacità.
Impostare la proprietà maxConcurrentOperationCount su 1. Questo trasforma OperationQueue in una coda sequenziale mantenendo tutti i vantaggi — dipendenze, priorità e cancellazione.
Il metodo cancel() imposta il flag isCancelled ma non ferma il metodo main() in esecuzione. Il codice dell'operazione deve verificare isCancelled da solo e terminare. La cancellazione funziona solo per le operazioni in attesa e pronte.
Dovresti ereditare da Operation quando hai bisogno di gestione dello stato, asincronia o riutilizzo della logica. BlockOperation è adatto per attività semplici una tantum senza ereditarietà.
No, a meno che non si chiami waitUntilFinished con il parametro true sul thread principale. Le operazioni vengono eseguite su thread di background per impostazione predefinita e i risultati vengono restituiti tramite OperationQueue.main.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche