Jak używać NSOperationQueue z NSURLSession?

Próbuję zbudować zbiorczy program do pobierania obrazów, w którym obrazy mogą być dodawane do kolejki w locie do pobrania, I mogę dowiedzieć się, jak postępują i kiedy kończą pobieranie.

Przez moje czytanie wydaje się NSOperationQueue dla funkcji kolejki i NSURLSession dla funkcjonalności sieci wydaje się najlepszym rozwiązaniem, ale jestem zdezorientowany, jak używać tych dwóch w tandemie.

Wiem, że dodaję instancje NSOperation do NSOperationQueue i zostają ustawione w kolejce. I wygląda na to, że tworzę zadanie pobierania z NSURLSessionDownloadTask i wielokrotnością, jeśli potrzebuję wielu zadań, ale nie jestem pewien, jak je połączyć.

NSURLSessionDownloadTaskDelegate wydaje się, że ma wszystkie informacje potrzebne do postępu pobierania i powiadomień o zakończeniu, ale muszę również być w stanie zatrzymać konkretne pobieranie, zatrzymać wszystkie pliki do pobrania i poradzić sobie z danymi, które otrzymuję z pobierania.

Author: Doug Smith, 2014-02-21

6 answers

Twoja intuicja tutaj jest poprawna. W przypadku wysyłania wielu żądań, posiadanie NSOperationQueue z maxConcurrentOperationCount 4 lub 5 może być bardzo przydatne. W przeciwnym razie, jeśli wyślesz wiele żądań (powiedzmy 50 dużych obrazów), możesz mieć problemy z limitem czasu podczas pracy na wolnym połączeniu sieciowym (np. niektóre połączenia komórkowe). Kolejki operacji mają też inne zalety (np. zależności, przypisywanie priorytetów itp.), ale kontrolowanie stopnia współbieżności jest kluczową korzyścią, IMHO.

Jeśli używasz Implementacja rozwiązań opartych na operacjach jest dość trywialna (jest to typowa implementacja podklasy współbieżnej NSOperation; zobacz sekcję Konfigurowanie operacji dla współbieżnego wykonywania w rozdziale Operation Queues w Concurrency Programming Guide aby uzyskać więcej informacji).

Jeśli używasz implementacji opartej na delegate, sprawy zaczynają się dość szybko układać. Jest to spowodowane zrozumiałym (ale niezwykle irytująca) Funkcja NSURLSession, w której delegaci na poziomie zadań są implementowani na poziomie sesji. (Pomyśl o tym: dwa różne żądania wymagające innej obsługi wywołują tę samą metodę delegata w obiekcie współdzielonej sesji. Egad!)

Owijanie delegata NSURLSessionTask W operację można wykonać (ja i inni, jestem pewien, że to zrobiłem), ale wiąże się to z nieporęcznym procesem utrzymywania obiektu sesji w słowniku porównującym identyfikatory zadań z task operation objects, aby przekazać te metody delegowania zadań przekazane do obiektu task, a następnie aby obiekty zadania były zgodne z różnymi protokołami delegatów NSURLSessionTask. Jest to dość znaczna ilość pracy wymaganej, ponieważ NSURLSession nie zapewnia funkcji w stylu maxConcurrentOperationCount w sesji (nie mówiąc o innych dobroci NSOperationQueue, takich jak zależności, bloki zakończenia itp.).

I warto zwrócić uwagę, że implementacja oparta na operacjach to trochę nie starter z tłem sesje. Zadania przesyłania/pobierania będą nadal działać dobrze po zakończeniu aplikacji (co jest dobrą rzeczą, jest to dość istotne zachowanie w żądaniu w tle), ale po ponownym uruchomieniu aplikacji Kolejka operacji i wszystkie jej operacje znikną. Więc musisz użyć czystej implementacji NSURLSession opartej na delegatach dla sesji w tle.

 40
Author: Rob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-23 22:00:33

Koncepcyjnie nsurlsession jest kolejką operacji. Jeśli wznowisz zadanie NSURLSession i punkt przerwania w obsłudze zakończenia, śledzenie stosu może być dość odkrywcze.

Oto fragment wiernego samouczka Raya Wenderlicha na NSURLSession z dodaną instrukcją NSLog do punktu przerwania podczas wykonywania obsługi zakończenia:

NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:londonWeatherUrl]
          completionHandler:^(NSData *data,
                              NSURLResponse *response,
                              NSError *error) {
            // handle response
            NSLog(@"Handle response"); // <-- breakpoint here       

  }] resume];

Nsoperationqueue Serial Queue breakpoint

Powyżej możemy zobaczyć procedurę obsługi zakończenia wykonywaną w Thread 5 Queue: NSOperationQueue Serial Queue.

Więc zgaduję, że każdy NSURLSession utrzymuje własną kolejkę operacji, a każde zadanie dodane do sesji jest - pod maską - wykonywane jako nsoperation. Dlatego nie ma sensu utrzymywanie kolejki operacji, która kontroluje obiekty NSURLSession lub zadania NSURLSession.

NSURLSessionTask już oferuje równoważne metody, takie jak cancel, resume, suspend, i tak dalej.

/ Align = "left" / Ale z drugiej strony, NSURLSession jest nowa klasa, której celem jest niewątpliwie uwolnienie Cię od tego ciężaru.

Podsumowując: jeśli chcesz mniej kłopotów - ale mniej kontroli - i ufasz Apple, że sprawnie wykonuje zadania sieciowe w Twoim imieniu, skorzystaj z NSURLSession. W przeciwnym razie wykonaj własne kolejki z NSURLConnection i własnymi kolejkami operacji.

 38
Author: Max MacLeod,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-25 16:42:45

Update: właściwości executing i finishing posiadają wiedzę o stanie bieżącego NSOperation. Po ustawieniu finishing na YES i executing na NO, Operacja jest uważana za zakończoną. Nie jest to jednak możliwe w przypadku, gdy nie jest to możliwe, ponieważ nie jest to możliwe w przypadku, gdy nie jest to możliwe.]}

  - (BOOL) isAsynchronous {
     return YES;
  }

  - (void) main
    {
       // We are starting everything
       self.executing = YES;
       self.finished = NO;

       NSURLSession * session = [NSURLSession sharedInstance];

       NSURL *url = [NSURL URLWithString:@"http://someurl"];

       NSURLSessionDataTask * dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){

          /* Do your stuff here */

         NSLog("Will show in second");

         self.executing = NO;
         self.finished = YES;
       }];

       [dataTask resume]
   }

Termin asynchronous jest dość mylący i nie odnosi się do rozróżnienia między wątkiem UI (głównym) i wątkiem tła.

Jeśli isAsynchronous jest ustawione na YES, oznacza to, że pewna część kodu jest wykonywana asynchronicznie w odniesieniu do metody main . Inaczej powiedziane: asynchroniczne wywołanie jest wykonywane wewnątrz metody main i metoda zakończy się po zakończeniu głównej metody .

Mam kilka slajdow o tym, jak radzić sobie z współbieżnością na apple os: https://speakerdeck.com/yageek/concurrency-on-darwin .

Stara odpowiedź: możesz spróbować dispatch_group_t. Można je uważać za zachowujące licznik dla GCD.

Wyobraź sobie poniższy kod w metodzie main Twojej podklasy NSOperation:

- (void) main
{

   self.executing = YES;
   self.finished = NO;

   // Create a group -> value = 0
   dispatch_group_t group = dispatch_group_create();

   NSURLSession * session = [NSURLSession sharedInstance];

   NSURL *url = [NSURL URLWithString:@"http://someurl"];

    // Enter the group manually -> Value = Value + 1
   dispatch_group_enter(group); ¨

   NSURLSessionDataTask * dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){


      /* Do your stuff here */

      NSLog("Will show in first");

      //Leave the group manually -> Value = Value - 1
      dispatch_group_leave(group);
   }];

   [dataTask resume];

  // Wait for the group's value to equals 0
  dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

  NSLog("Will show in second");

  self.executing = NO;
  self.finished = YES;
}
 6
Author: yageek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-06 14:15:57

Z NSURLSession nie dodajesz ręcznie żadnych operacji do kolejki. Używasz metody - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request Na NSURLSession do wygenerowania zadania z danymi, które następnie uruchamiasz (wywołując metodę resume).

Możesz podać kolejkę operacji, dzięki czemu możesz kontrolować właściwości kolejki, a także używać jej do innych operacji, jeśli chcesz.

Wszelkie typowe działania, które chcesz podjąć na Nsoperacji (tj. start, pauza, stop, wznowienie) wykonujesz na danych zadanie.

Aby ustawić w kolejce 50 obrazów do pobrania, możesz po prostu utworzyć 50 zadań danych, które nsurlsession będzie prawidłowo ustawiać w kolejce.

 2
Author: Reid Main,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-20 21:49:04

Jeśli używasz OperationQueue i nie chcesz, aby każda operacja tworzyła wiele jednoczesnych żądań sieciowych, możesz po prostu kolejkować połączenia.waitUntilAllOperationsAreFinished () po dodaniu każdej operacji do kolejki. Będą one teraz wykonywane dopiero po zakończeniu poprzedniego, znacznie zmniejszając ilość jednoczesnych połączeń sieciowych.

 0
Author: Above The Gods,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-09 12:43:39

Może tego szukasz:

Http://www.dribin.org/dave/blog/archives/2009/05/05/concurrent_operations/

To trochę dziwne, że to nie jest "wbudowane" , ale jeśli chcesz podłączyć rzeczy NSURL z NSOperation to wygląda na to, że musisz ponownie użyć runloopa w głównym wątku i uczynić operację "współbieżną" ("współbieżną" do kolejki).

Choć w Twoim przypadku-jeśli chodzi tylko o zwykłe pobieranie, bez kolejnych, zależnych, operacji podłączony - nie jestem pewien, co można zyskać przy użyciu NSOperation.

 -1
Author: hnh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-26 22:44:02