NSOperation vs Grand Central Dispatch

465

Uczę się o programowaniu współbieżnym na iOS. Do tej pory czytałem o NSOperation/NSOperationQueue i GCD. Jakie są powody używania NSOperationQueueover GCDi vice versa?

Brzmi jak oba GCDi NSOperationQueueoddziela jawne tworzenie NSThreadsod użytkownika. Jednak związek między tymi dwoma podejściami nie jest dla mnie jasny, więc wszelkie uwagi należy docenić!

Niedziela poniedziałek
źródło
10
+1 za dobre pytanie - ciekawy wyników. Dotychczas czytałem, że GCD można łatwo wysyłać między rdzeniami procesora, co czyni go „nowym gównem”.
Do
3
Powiązaną dyskusję można znaleźć w tym pytaniu: Dlaczego powinienem wybrać GCD zamiast NSOperation i bloków dla aplikacji wysokiego poziomu?
Brad Larson

Odpowiedzi:

517

GCDto niskopoziomowy interfejs API oparty na języku C, który umożliwia bardzo proste korzystanie z modelu współbieżności opartego na zadaniach. NSOperationi NSOperationQueuesą klasami Objective-C, które robią podobne rzeczy. NSOperationzostał wprowadzony jako pierwszy, ale od wersji 10.5 i iOS 2 , NSOperationQueuea znajomi są wdrażani wewnętrznie za pomocą GCD.

Ogólnie powinieneś używać najwyższego poziomu abstrakcji, który odpowiada Twoim potrzebom. Oznacza to, że zwykle powinieneś używać NSOperationQueuezamiast GCD, chyba że musisz zrobić coś, NSOperationQueueco nie obsługuje.

Zauważ, że NSOperationQueuenie jest to „głupia” wersja GCD; w rzeczywistości istnieje wiele rzeczy, które możesz zrobić w bardzo prosty sposób, NSOperationQueuektóre wymagają dużo pracy z czystym GCD. (Przykłady: kolejki o ograniczonej przepustowości, które uruchamiają tylko operacje N na raz; ustalanie zależności między operacjami. Obie są bardzo proste NSOperation, bardzo trudne z GCD.) Apple ciężko pracowało nad wykorzystaniem GCD do stworzenia bardzo ładnego, przyjaznego obiektowo interfejsu API NSOperation. Skorzystaj z ich pracy, chyba że masz powód, aby tego nie robić.

Zastrzeżenie : Z drugiej strony, jeśli naprawdę musisz po prostu wysłać blok i nie potrzebujesz żadnej dodatkowej funkcjonalności, która NSOperationQueuezapewnia, nie ma nic złego w korzystaniu z GCD. Tylko upewnij się, że jest to odpowiednie narzędzie do pracy.

BJ Homer
źródło
1
NSOperation to konkretna klasa abstrakcyjna.
Roshan
3
@Sandy W rzeczywistości jest odwrotnie, GSO jest używany przez NSOperation (przynajmniej w późniejszych wersjach iOS i OS X).
garrettmoon
1
@BJ Homer Możemy dodać zadanie w kolejce wysyłek seryjnych do szybkiego depeancy. więc uzasadnij, w jaki sposób kolejka operacji ma nad tym przewagę
Raj Aggrawal
3
@RajAggrawal Tak, to działa… ale utkniesz w kolejce szeregowej. NSOperation może wykonać „wykonać tę operację po wykonaniu pozostałych trzech, ale jednocześnie z wszystkimi innymi działającymi sprawami”. Zależności operacji mogą istnieć nawet między operacjami w różnych kolejkach. Większość ludzi nie będzie tego potrzebować, ale jeśli to zrobisz, NSOperation byłby lepszym wyborem.
BJ Homer,
369

Zgodnie z moją odpowiedzią na powiązane pytanie zamierzam się nie zgodzić z BJ i zasugerować, aby najpierw spojrzeć na GCD przez NSOperation / NSOperationQueue, chyba że to drugie zapewnia coś, czego GCD nie potrzebuje.

Przed GCD korzystałem z wielu aplikacji NSOperations / NSOperationQueues w swoich aplikacjach do zarządzania współbieżnością. Jednak odkąd zacząłem używać GCD regularnie, prawie całkowicie zastąpiłem NSOperations i NSOperationQueues blokami i kolejkami wysyłki. Wynika to z tego, jak korzystałem z obu technologii w praktyce oraz z profilowania, które na nich przeprowadziłem.

Po pierwsze, korzystanie z NSOperations i NSOperationQueues wiąże się z nietrywialnym obciążeniem. Są to obiekty kakaowe i należy je przydzielić i zwolnić. W napisanej przeze mnie aplikacji na iOS, która renderuje trójwymiarową scenę przy 60 FPS, używałem NSOperations do enkapsulacji każdej renderowanej ramki. Kiedy profilowałem to, tworzenie i porzucanie tych NSOperations odpowiadało za znaczną część cykli procesora w uruchomionej aplikacji i spowalniało rzeczy. Zastąpiłem je prostymi blokami i kolejką szeregową GCD, a ten narzut zniknął, co prowadzi do zauważalnie lepszej wydajności renderowania. To nie było jedyne miejsce, w którym zauważyłem narzut dzięki korzystaniu z NSOperations i widziałem to zarówno na Macu, jak i iOS.

Po drugie, blokowy kod wysyłki ma elegancję, którą trudno dopasować przy użyciu NSOperations. Bardzo wygodne jest zawinięcie kilku wierszy kodu w blok i wysłanie go do wykonania w kolejce szeregowej lub współbieżnej, gdzie utworzenie niestandardowego NSOperation lub NSInvocationOperation wymaga o wiele więcej kodu pomocniczego. Wiem, że możesz użyć NSBlockOperation, ale równie dobrze możesz wtedy wysłać coś do GCD. Zawijanie tego kodu w bloki w powiązaniu z powiązanym przetwarzaniem w twojej aplikacji prowadzi moim zdaniem do lepszej organizacji kodu niż posiadanie osobnych metod lub niestandardowych NSOperations, które zawierają te zadania.

NSOperations i NSOperationQueues nadal mają bardzo dobre zastosowania. GCD nie ma prawdziwej koncepcji zależności, w której NSOperationQueues może skonfigurować dość złożone wykresy zależności. Używam do tego NSOperationQueues w kilku przypadkach.

Ogólnie rzecz biorąc, chociaż zwykle opowiadam się za wykorzystaniem najwyższego poziomu abstrakcji, który spełnia zadanie, jest to jeden przypadek, w którym opowiadam się za niższym poziomem API GCD. Spośród programistów iOS i Mac, z którymi rozmawiałem na ten temat, zdecydowana większość decyduje się na użycie GCD zamiast NSOperations, chyba że są ukierunkowane na wersje systemu operacyjnego bez obsługi tego systemu (te wcześniejsze niż iOS 4.0 i Snow Leopard).

Brad Larson
źródło
20
Ja tylko nieznacznie się nie zgadzam; Dość często używam zwykłego GCD. Ale myślę, że zbyt mocno odrzucasz NSBlockOperation w tej odpowiedzi. Wszystkie zalety NSOperationQueue (zależności, debugowanie itp.) Dotyczą także operacji blokowych.
BJ Homer,
4
@BJHomer - Myślę, że unikanie NSBlockOperation jest w moim przypadku bardziej kwestią osobistych preferencji, chociaż w ogóle unikałem NSOperations po tym, jak zobaczyłem, że narzut z ich użycia przeciągnął kilka aplikacji. Jeśli zamierzam używać bloków, mam tendencję do wchodzenia all-in na GCD, z rzadkim wyjątkiem, gdy potrzebuję wsparcia zależności.
Brad Larson
1
+1, dziękuję za tę analizę. Apple wydaje się opowiadać za obiema (jak sesja WWDC 2012 na temat jednoczesnego interfejsu użytkownika), więc jest to bardzo cenione.
orip
1
@VolureDarkAngel - GCD jest wyjątkowo szybki w obsłudze takich wysyłek. Nie powinno to być twoim wąskim gardłem w sytuacji, którą opisujesz, chyba że w jakiś sposób utworzysz kopie zapasowe aktualizacji w kolejce ze względu na powolny dostęp do I / O lub coś w tym rodzaju. Prawdopodobnie jednak tak nie jest.
Brad Larson
1
@ asma22 - Często wykonuje się obliczenia, które można wykonać w porcjach, ale ostateczne obliczenia jednego etapu mogą wymagać wyników z kilku poprzednich etapów. W takim przypadku można uzależnić późniejszą operację od wcześniejszych operacji, a harmonogram będzie zarządzany w taki sposób, aby wszystkie zostały zakończone przed uruchomieniem ostatniej.
Brad Larson
101

GCDjest niskopoziomowym interfejsem API opartym na C.
NSOperationi NSOperationQueuesą klasami Objective-C.
NSOperationQueueobiektywne C owijka GCD. Jeśli używasz NSOperation, to domyślnie używasz Grand Central Dispatch.

Przewaga GCD nad NSOperation:
i. Realizacja
Dla GCDrealizacji jest bardzo lekki
NSOperationQueuejest złożony i heavy-weight

Zalety NSOperation w porównaniu z GCD:

ja. Kontroluj działanie
można wstrzymać, anulować, wznowićNSOperation

ii. Zależności,
które można ustawić, zależność między dwiema NSOperations
operacjami nie rozpocznie się, dopóki wszystkie jej zależności nie zwrócą wartości true dla zakończenia.

iii. Stan operacji
może monitorować stan operacji lub kolejki operacji. gotowe, wykonane lub zakończone

iv. Maksymalna liczba operacji
Można określić maksymalną liczbę operacji w kolejce, które mogą być uruchomione jednocześnie

Kiedy iść GCDlubNSOperation
kiedy chcesz mieć większą kontrolę nad kolejką (wszystkie wyżej wymienione), NSOperation a także w prostych przypadkach, w których chcesz zmniejszyć obciążenie (po prostu chcesz wykonać trochę pracy „w tle” przy bardzo małej dodatkowej pracy)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
źródło
Jak powiedziano, maksymalną liczbę operacji można określić w NSOperationQueue, a jaka może być maksymalna liczba operacji (kolejek wysyłkowych) w GCD? Załóżmy, że mam projekt, a następnie ile operacji (kolejek wysyłkowych) mogę wykonać. lub ich maksymalne limity, które możemy zrobić.
Roshan Sah
Zależy to od warunków systemowych tutaj są szczegółowe informacje: stackoverflow.com/questions/14995801/...
Sangram Shivankar
Możemy także anulować zadanie w GCD za pomocą DispatchWorkItem, a także możemy zawiesić i wznowić
Ankit garg
@Ankitgarg Wywołanie anuluj na DispatchWorkItem zatrzyma wykonywanie zadań, jeśli jeszcze nie zostały uruchomione, ale nie zatrzyma czegoś, co już działa. i jak wstrzymujesz / wznawiasz DispatchWorkItem?
abhimuralidharan
34

Innym powodem preferowania NSOperation nad GCD jest mechanizm anulowania NSOperation. Na przykład aplikacja taka jak 500px, która pokazuje dziesiątki zdjęć, używając NSOperation możemy anulować żądania niewidocznych komórek obrazu, gdy przewijamy widok tabeli lub widok kolekcji, może to znacznie poprawić wydajność aplikacji i zmniejszyć zużycie pamięci. GCD nie może tego łatwo obsłużyć.

Również dzięki NSOperation KVO może być możliwe.

Oto artykuł z Eschaton, który warto przeczytać.

evanchin
źródło
4
Warto zauważyć, że jeśli to, czego anulowanie jest operacja sieć ładowania obrazu, wtedy nie trzeba NSOperationdo tego, jak NSURLSessionTask.canceli NSURLSession.invalidateAndCancelzapewnienia tej funkcji. Na ogół, NSURLSessionstanowi część funkcjonalność NSOperationQueue, co NSURLSessionTaskstanowi część funkcjonalnośćNSOperation
glonów
@algal Jak wyjaśniono tutaj ( stackoverflow.com/questions/21918722/... ), wydaje się, że NSURLSession używa NSOperationQueue jako bloku konstrukcyjnego.
kalan nawarathne
33

GCD jest rzeczywiście na niższym poziomie niż NSOperationQueue, jego główną zaletą jest to, że jego implementacja jest bardzo lekka i skupia się na algorytmach bez blokady i wydajności.

NSOperationQueue zapewnia funkcje, które nie są dostępne w GCD, ale wiążą się one z niebanalnymi kosztami, wdrożenie NSOperationQueue jest złożone i ciężkie, wymaga dużej ilości blokowania i używa GCD wewnętrznie tylko w bardzo minimalny sposób.

Jeśli potrzebujesz udogodnień zapewnianych przez NSOperationQueue, skorzystaj z niego, ale jeśli GCD jest wystarczający dla twoich potrzeb, zaleciłbym użycie go bezpośrednio dla lepszej wydajności, znacznie niższego kosztu procesora i energii oraz większej elastyczności.

das
źródło
24

Zarówno NSQueueOperations, jak i GCD umożliwiają wykonywanie ciężkich zadań obliczeniowych w tle na osobnych wątkach poprzez zwolnienie głównego profilu aplikacji interfejsu użytkownika.

Cóż, w oparciu o poprzedni post widzimy, że NSOperations ma addDependency, dzięki czemu możesz kolejkować swoje operacje jedna po drugiej.

Ale czytam również o kolejkach szeregowych GCD, które możesz utworzyć, uruchamiaj swoje operacje w kolejce za pomocą dispatch_queue_create. Umożliwi to uruchamianie zestawu operacji jeden po drugim w sekwencyjny sposób.

Zalety NSQueueOperation w porównaniu z GCD:

  1. Pozwala na dodanie zależności i pozwala na usunięcie zależności, więc dla jednej transakcji można uruchomić sekwencyjnie przy użyciu zależności, a dla drugiej transakcji jednocześnie, podczas gdy GCD nie pozwala na uruchomienie w ten sposób.

  2. Można łatwo anulować operację, jeśli znajduje się ona w kolejce, można ją zatrzymać, jeśli jest uruchomiona.

  3. Możesz zdefiniować maksymalną liczbę jednoczesnych operacji.

  4. Możesz zawiesić operacje, które znajdują się w kolejce

  5. Możesz sprawdzić, ile jest oczekujących operacji w kolejce.

Shashi3456643
źródło
6

GCD jest bardzo łatwy w użyciu - jeśli chcesz zrobić coś w tle, wszystko, co musisz zrobić, to napisać kod i wysłać go w kolejce w tle. Robienie tego samego z NSOperation to dużo dodatkowej pracy.

Zaletą NSOperation jest to, że (a) masz prawdziwy obiekt, do którego możesz wysyłać wiadomości, oraz (b) możesz anulować NSOperation. To nie jest trywialne. Musisz podklasować NSOperation, musisz poprawnie napisać kod, aby anulowanie i prawidłowe zakończenie zadania działały poprawnie. Więc dla prostych rzeczy używasz GCD, a dla bardziej skomplikowanych rzeczy tworzysz podklasę NSOperation. (Istnieją podklasy NSInvocationOperation i NSBlockOperation, ale wszystko, co robią, jest łatwiejsze do zrobienia dzięki GCD, więc nie ma dobrego powodu, aby z nich korzystać).

gnasher729
źródło
3

Cóż, NSOperations to po prostu interfejs API zbudowany na bazie Grand Central Dispatch. Więc kiedy używasz NSOperations, naprawdę nadal używasz Grand Central Dispatch. Po prostu NSOperations daje ci kilka fantazyjnych funkcji, które mogą ci się spodobać. Możesz uzależnić niektóre operacje od innych, zmienić kolejkę po sumbitowaniu przedmiotów i inne podobne rzeczy. W rzeczywistości ImageGrabber już używa NSOperations i kolejek operacyjnych! ASIHTTPRequest używa ich pod maską, a jeśli chcesz, możesz skonfigurować kolejkę operacji, której używa, dla różnych zachowań. Którego powinieneś użyć? Cokolwiek ma sens dla Twojej aplikacji. W przypadku tej aplikacji jest to dość proste, więc użyliśmy bezpośrednio Grand Central Dispatch, bez potrzeby fantazyjnych funkcji NSOperation. Ale jeśli potrzebujesz ich do swojej aplikacji, skorzystaj z niej!

Ankul Gaur
źródło