NSOperation gegen Grand Central Dispatch

465

Ich lerne etwas über gleichzeitiges Programmieren für iOS. Bisher habe ich über NSOperation/NSOperationQueue und gelesen GCD. Was sind die Gründe für die Verwendung von NSOperationQueueover GCDund umgekehrt?

Klingt nach beidem GCDund NSOperationQueueabstrahiert die explizite Erstellung NSThreadsvom Benutzer. Die Beziehung zwischen den beiden Ansätzen ist mir jedoch nicht klar, so dass jedes Feedback geschätzt wird!

Sonntag Montag
quelle
10
+1 für gute Frage - neugierig auf die Ergebnisse. Bisher habe ich gerade gelesen, dass GCD problemlos über CPU-Kerne verteilt werden kann, was es zur "neuen heißen Scheiße" macht.
Bis zum
3
Einige verwandte Diskussionen finden Sie in dieser Frage: Warum sollte ich GCD anstelle von NSOperation und Blöcken für Anwendungen auf hoher Ebene wählen?
Brad Larson

Antworten:

517

GCDist eine C-basierte API auf niedriger Ebene, die die sehr einfache Verwendung eines aufgabenbasierten Parallelitätsmodells ermöglicht. NSOperationund NSOperationQueuesind Objective-C-Klassen, die etwas Ähnliches tun. NSOperationwurde zuerst eingeführt, aber ab 10.5 und iOS 2 , NSOperationQueueund Freunde werden intern mit implementiert GCD.

Im Allgemeinen sollten Sie die höchste Abstraktionsebene verwenden, die Ihren Anforderungen entspricht. Dies bedeutet, dass Sie normalerweise NSOperationQueueanstelle von verwenden sollten GCD, es sei denn, Sie müssen etwas tun, NSOperationQueuedas nicht unterstützt wird.

Beachten Sie, dass dies NSOperationQueuekeine "heruntergekommene" Version von GCD ist. In der Tat gibt es viele Dinge, die Sie sehr einfach tun können NSOperationQueue, die viel Arbeit mit pur erfordern GCD. (Beispiele: Warteschlangen mit eingeschränkter Bandbreite, die jeweils nur N Operationen ausführen; Abhängigkeiten zwischen Operationen herstellen. Beides sehr einfach NSOperation, sehr schwierig GCD.) Apple hat die harte Arbeit geleistet, GCD zu nutzen, um eine sehr schöne objektfreundliche API mit zu erstellen NSOperation. Nutzen Sie ihre Arbeit, es sei denn, Sie haben einen Grund, dies nicht zu tun.

Vorsichtsmaßnahme : Wenn Sie jedoch wirklich nur einen Block senden müssen und keine der zusätzlichen Funktionen benötigen, die NSOperationQueuebereitgestellt werden, ist die Verwendung von GCD nichts Falsches. Stellen Sie nur sicher, dass es das richtige Werkzeug für den Job ist.

BJ Homer
quelle
1
NSOperation um eine abstrakte Klasse zu sein.
Roshan
3
@ Sandy Es ist eigentlich das Gegenteil, GCD wird von NSOperation verwendet (zumindest in späteren Versionen von iOS und OS X).
Garrettmoon
1
@BJ Homer Wir können eine Aufgabe in der Warteschlange für den seriellen Versand hinzufügen, um die Abhängigkeit zu erreichen. So gerecht, wie die Operationswarteschlange einen Vorteil gegenüber dem hat
Raj Aggrawal
3
@ RajAggrawal Ja, das funktioniert ... aber dann stecken Sie in einer seriellen Warteschlange. NSOperation kann "diesen Vorgang ausführen, nachdem die anderen drei ausgeführt wurden, aber gleichzeitig mit allen anderen Vorgängen". Operationsabhängigkeiten können sogar zwischen Operationen in verschiedenen Warteschlangen bestehen. Die meisten Leute werden das nicht brauchen, aber wenn Sie dies tun, wäre NSOperation die bessere Wahl.
BJ Homer
369

In Übereinstimmung mit meiner Antwort auf eine verwandte Frage werde ich BJ nicht zustimmen und vorschlagen, dass Sie sich zuerst GCD über NSOperation / NSOperationQueue ansehen, es sei denn, letzteres bietet etwas, das Sie benötigen, das GCD nicht benötigt.

Vor GCD habe ich in meinen Anwendungen viele NSOperations / NSOperationQueues zum Verwalten der Parallelität verwendet. Seit ich GCD regelmäßig verwende, habe ich NSOperations und NSOperationQueues fast vollständig durch Blöcke und Versandwarteschlangen ersetzt. Dies ist darauf zurückzuführen, wie ich beide Technologien in der Praxis eingesetzt habe und wie ich sie profiliert habe.

Erstens entsteht bei der Verwendung von NSOperations und NSOperationQueues ein nicht trivialer Overhead. Dies sind Kakaoobjekte, die zugewiesen und freigegeben werden müssen. In einer von mir geschriebenen iOS-Anwendung, die eine 3D-Szene mit 60 FPS rendert, habe ich NSOperations verwendet, um jeden gerenderten Frame zu kapseln. Als ich dies profilierte, machte das Erstellen und Herunterfahren dieser NSOperations einen erheblichen Teil der CPU-Zyklen in der laufenden Anwendung aus und verlangsamte die Dinge. Ich habe diese durch einfache Blöcke und eine serielle GCD-Warteschlange ersetzt, und dieser Overhead verschwand, was zu einer deutlich besseren Renderleistung führte. Dies war nicht der einzige Ort, an dem ich durch die Verwendung von NSOperations einen Overhead bemerkte, und ich habe dies sowohl auf Mac als auch auf iOS gesehen.

Zweitens hat blockbasierter Versandcode eine Eleganz, die bei Verwendung von NSOperations nur schwer zu erreichen ist. Es ist unglaublich praktisch, ein paar Codezeilen in einen Block zu packen und ihn in einer seriellen oder gleichzeitigen Warteschlange zu versenden, wo das Erstellen einer benutzerdefinierten NSOperation oder NSInvocationOperation viel mehr unterstützenden Code erfordert. Ich weiß, dass Sie eine NSBlockOperation verwenden können, aber Sie könnten dann genauso gut etwas an GCD senden. Das Umschließen dieses Codes in Blöcke im Einklang mit der zugehörigen Verarbeitung in Ihrer Anwendung führt meiner Meinung nach zu einer besseren Codeorganisation als separate Methoden oder benutzerdefinierte NSOperations, die diese Aufgaben kapseln.

NSOperations und NSOperationQueues haben immer noch sehr gute Verwendungsmöglichkeiten. GCD hat kein wirkliches Konzept von Abhängigkeiten, bei denen NSOperationQueues ziemlich komplexe Abhängigkeitsgraphen erstellen kann. In einigen Fällen verwende ich dafür NSOperationQueues.

Insgesamt plädiere ich zwar für die Verwendung der höchsten Abstraktionsebene, die die Aufgabe erfüllt, aber dies ist ein Fall, in dem ich mich für die untergeordnete API von GCD ausspreche. Unter den iOS- und Mac-Entwicklern, mit denen ich darüber gesprochen habe, wählt die überwiegende Mehrheit GCD anstelle von NSOperations, es sei denn, sie zielen auf Betriebssystemversionen ab, die dies nicht unterstützen (jene vor iOS 4.0 und Snow Leopard).

Brad Larson
quelle
20
Ich bin nur leicht anderer Meinung; Ich benutze ziemlich viel einfaches GCD. Aber ich denke, Sie rabattieren NSBlockOperation in dieser Antwort zu stark. Alle Vorteile von NSOperationQueue (Abhängigkeiten, Debugbarkeit usw.) gelten auch für Blockierungsvorgänge.
BJ Homer
4
@BJHomer - Ich denke, die Vermeidung von NSBlockOperation ist in meinem Fall eher eine Frage der persönlichen Präferenz, obwohl ich mich generell von NSOperations ferngehalten habe, nachdem ich gesehen habe, dass der Aufwand durch ihre Verwendung einige Anwendungen nach unten gezogen hat. Wenn ich Blöcke verwenden möchte, gehe ich in der Regel All-in auf GCD, mit der seltenen Ausnahme, dass ich Abhängigkeitsunterstützung benötige.
Brad Larson
1
+1, danke für diese Analyse. Apple scheint beides zu befürworten (wie die Sitzung der WWDC 2012 zur gleichzeitigen Benutzeroberfläche), daher wird dies sehr geschätzt.
Orip
1
@VolureDarkAngel - GCD ist extrem schnell im Umgang mit solchen Sendungen. Es sollte nicht Ihr Engpass in einer Situation sein, wie Sie sie beschreiben, es sei denn, Sie sichern einen Stapel von Updates aufgrund langsamer E / A-Zugriffe oder Ähnlichem in einer Warteschlange. Dies ist hier jedoch wahrscheinlich nicht der Fall.
Brad Larson
1
@ asma22 - Es ist üblich, Berechnungen durchzuführen, die in Blöcken durchgeführt werden können, aber für die endgültige Berechnung einer Stufe sind möglicherweise die Ergebnisse mehrerer vorheriger Stufen erforderlich. In diesem Fall können Sie festlegen, dass der spätere Vorgang von den früheren Vorgängen abhängt, und die Planung wird so verwaltet, dass alle Vorgänge abgeschlossen sind, bevor der letzte ausgeführt wird.
Brad Larson
101

GCDist eine C-basierte API auf niedriger Ebene.
NSOperationund NSOperationQueuesind Objective-C-Klassen.
NSOperationQueueist objektiv C Wrapper vorbei GCD. Wenn Sie NSOperation verwenden, verwenden Sie implizit Grand Central Dispatch.

GCD-Vorteil gegenüber NSOperation:
i. Implementierung
Für die GCDImplementierung ist sehr leicht,
NSOperationQueuekomplex und schwer

Vorteile von NSOperation gegenüber GCD:

ich. Control On Operation können
Sie anhalten, abbrechen, fortsetzenNSOperation

ii. Abhängigkeiten, mit denen
Sie eine Abhängigkeit zwischen zwei NSOperations
Vorgängen einrichten können, werden erst gestartet, wenn alle Abhängigkeiten true für "beendet" zurückgegeben haben.

iii. Der Betriebsstatus
kann den Status eines Vorgangs oder einer Betriebswarteschlange überwachen. bereit, ausführend oder fertig

iv.
Maximale Anzahl von Vorgängen Sie können die maximale Anzahl von Vorgängen in der Warteschlange angeben, die gleichzeitig ausgeführt werden können

Wann GCDoderNSOperation
wann Sie mehr Kontrolle über die Verwendung der Warteschlange (alle oben genannten) NSOperation und für einfache Fälle, in denen Sie weniger Overhead wünschen (Sie möchten nur etwas Arbeit "im Hintergrund" mit sehr wenig zusätzlicher Arbeit erledigen)GCD

Ref:
https://cocoacasts.com/choosing-between-nsoperation-and-grand-central-dispatch/ http://iosinfopot.blogspot.in/2015/08/nsthread-vs-gcd-vs-nsoperationqueue.html http : //nshipster.com/nsoperation/

Sangram Shivankar
quelle
Wie bereits erwähnt, kann die maximale Anzahl von Vorgängen in NSOperationQueue angegeben werden. Wie hoch kann dann die maximale Anzahl von Vorgängen (Versandwarteschlangen) in GCD sein? Angenommen, ich habe ein Projekt. Wie viele Vorgänge (Versandwarteschlangen) kann ich dann ausführen? oder es gibt irgendwelche Höchstgrenzen, bis zu denen wir tun können.
Roshan Sah
Es hängt von den Systembedingungen ab, hier finden Sie detaillierte Informationen: stackoverflow.com/questions/14995801/…
Sangram Shivankar
Wir können die Aufgabe auch in GCD mit DispatchWorkItem abbrechen und wir können auch aussetzen und fortsetzen
Ankit garg
@Ankitgarg Wenn Sie Abbrechen auf DispatchWorkItem aufrufen, wird die Ausführung von Aufgaben gestoppt, wenn sie noch nicht ausgeführt wurden, aber etwas, das bereits ausgeführt wird, wird nicht angehalten. und wie pausiert / setzt man ein DispatchWorkItem fort?
Abhimuralidharan
34

Ein weiterer Grund, NSOperation gegenüber GCD zu bevorzugen, ist der Aufhebungsmechanismus von NSOperation. Beispiel: Bei einer App wie 500px, die Dutzende von Fotos anzeigt, können Sie mithilfe von NSOperation Anforderungen von unsichtbaren Bildzellen abbrechen, wenn wir durch die Tabellen- oder Sammlungsansicht scrollen. Dies kann die App-Leistung erheblich verbessern und den Speicherbedarf verringern. GCD kann dies nicht einfach unterstützen.

Auch mit NSOperation kann KVO möglich sein.

Hier ist ein Artikel von Eschaton, der es wert ist, gelesen zu werden.

Evanchin
quelle
4
Es ist erwähnenswert, dass , wenn , was Sie Aufheben der Betrieb Netzwerk ist , das Bild des Ladens, dann brauchen Sie nicht NSOperationdafür, wie NSURLSessionTask.cancelund NSURLSession.invalidateAndCanceldiese Funktionalität bereitstellen. Im Allgemeinen NSURLSessionstellt einen Teil der Funktionalität eines NSOperationQueue, als NSURLSessionTaskeinen Teil der Funktionalität eines bietetNSOperation
Algen
@algal Wie hier erläutert ( stackoverflow.com/questions/21918722/… ), scheint NSURLSession NSOperationQueue als Baustein zu verwenden.
Kalan Nawarathne
33

GCD ist in der Tat niedriger als NSOperationQueue. Sein Hauptvorteil besteht darin, dass seine Implementierung sehr leicht ist und sich auf sperrfreie Algorithmen und Leistung konzentriert.

NSOperationQueue bietet Funktionen, die in GCD nicht verfügbar sind, die jedoch nicht trivial sind. Die Implementierung von NSOperationQueue ist komplex und schwer, erfordert viel Sperren und verwendet GCD intern nur in sehr geringem Umfang.

Wenn Sie die von NSOperationQueue bereitgestellten Funktionen benötigen, verwenden Sie sie auf jeden Fall. Wenn GCD jedoch für Ihre Anforderungen ausreicht, würde ich empfehlen, es direkt zu verwenden, um eine bessere Leistung, deutlich niedrigere CPU- und Stromkosten sowie mehr Flexibilität zu erzielen.

das
quelle
24

Sowohl NSQueueOperations als auch GCD ermöglichen die Ausführung einer umfangreichen Rechenaufgabe im Hintergrund in separaten Threads, indem der Hauptbereich der UI-Anwendung freigegeben wird.

Basierend auf dem vorherigen Beitrag sehen wir, dass NSOperations addDependency hat, so dass Sie Ihre Operation nacheinander in die Warteschlange stellen können.

Ich habe aber auch über serielle GCD-Warteschlangen gelesen, die Sie erstellen können, um Ihre Vorgänge in der Warteschlange mit dispatch_queue_create auszuführen. Auf diese Weise können Sie eine Reihe von Vorgängen nacheinander ausführen.

Vorteile von NSQueueOperation gegenüber GCD:

  1. Es ermöglicht das Hinzufügen von Abhängigkeiten und das Entfernen von Abhängigkeiten, sodass Sie für eine Transaktion sequenziell mithilfe von Abhängigkeiten und für andere Transaktionen gleichzeitig ausgeführt werden können, während GCD die Ausführung auf diese Weise nicht zulässt.

  2. Es ist einfach, einen Vorgang abzubrechen, wenn er sich in der Warteschlange befindet. Er kann gestoppt werden, wenn er ausgeführt wird.

  3. Sie können die maximale Anzahl gleichzeitiger Vorgänge definieren.

  4. Sie können den Vorgang in der Warteschlange anhalten

  5. Sie können feststellen, wie viele ausstehende Vorgänge sich in der Warteschlange befinden.

Shashi3456643
quelle
6

GCD ist sehr einfach zu bedienen. Wenn Sie etwas im Hintergrund tun möchten, müssen Sie nur den Code schreiben und ihn in eine Hintergrundwarteschlange stellen. Dasselbe mit NSOperation zu tun, ist eine Menge zusätzlicher Arbeit.

Der Vorteil von NSOperation besteht darin, dass (a) Sie ein reales Objekt haben, an das Sie Nachrichten senden können, und (b) dass Sie eine NSOperation abbrechen können. Das ist nicht trivial. Sie müssen NSOperation in eine Unterklasse unterteilen. Sie müssen Ihren Code korrekt schreiben, damit das Abbrechen und das korrekte Beenden einer Aufgabe ordnungsgemäß funktionieren. Für einfache Dinge verwenden Sie GCD und für kompliziertere Dinge erstellen Sie eine Unterklasse von NSOperation. (Es gibt Unterklassen NSInvocationOperation und NSBlockOperation, aber alles, was sie tun, ist mit GCD einfacher, sodass es keinen guten Grund gibt, sie zu verwenden.)

gnasher729
quelle
3

Nun, NSOperations sind einfach eine API, die auf Grand Central Dispatch basiert. Wenn Sie also NSOperations verwenden, verwenden Sie immer noch Grand Central Dispatch. Es ist nur so, dass NSOperations Ihnen einige ausgefallene Funktionen bietet, die Ihnen gefallen könnten. Sie können einige Vorgänge von anderen Vorgängen abhängig machen, Warteschlangen nach dem Versenden von Elementen neu anordnen und ähnliche Dinge. Tatsächlich verwendet ImageGrabber bereits NSOperations und Operationswarteschlangen! ASIHTTPRequest verwendet sie unter der Haube, und Sie können die verwendete Operationswarteschlange für ein anderes Verhalten konfigurieren, wenn Sie möchten. Also was solltest du verwenden? Was auch immer für Ihre App sinnvoll ist. Für diese App ist es ziemlich einfach, also haben wir Grand Central Dispatch direkt verwendet, ohne die ausgefallenen Funktionen von NSOperation zu benötigen. Aber wenn Sie sie für Ihre App benötigen, können Sie sie gerne verwenden!

Ankul Gaur
quelle