Nie wiem, jak i kiedy używać beginBackgroundTaskWithExpirationHandler
.
Apple pokazuje na swoich przykładach, aby użyć go jako applicationDidEnterBackground
delegata, aby uzyskać więcej czasu na wykonanie ważnego zadania, zwykle transakcji sieciowej.
Patrząc na moją aplikację, wydaje się, że większość rzeczy w mojej sieci jest ważna, a kiedy ktoś jest uruchomiony, chciałbym je zakończyć, jeśli użytkownik nacisnął przycisk home.
Czy zatem jest akceptowaną / dobrą praktyką zawijanie każdej transakcji sieciowej (i nie mówię o pobieraniu dużych porcji danych, głównie krótkiego pliku xml), beginBackgroundTaskWithExpirationHandler
aby być po bezpiecznej stronie?
Odpowiedzi:
Jeśli chcesz, aby transakcja sieciowa była kontynuowana w tle, musisz ją opakować w zadanie w tle. Bardzo ważne jest również, aby zadzwonić
endBackgroundTask
po zakończeniu - w przeciwnym razie aplikacja zostanie zabita po upływie wyznaczonego czasu.Mój zwykle wygląda mniej więcej tak:
Mam
UIBackgroundTaskIdentifier
właściwość dla każdego zadania w tleRównoważny kod w Swift
źródło
Przyjęta odpowiedź jest bardzo pomocna i w większości przypadków powinna wystarczyć, jednak martwiły mnie dwie rzeczy:
Jak zauważyło wiele osób, przechowywanie identyfikatora zadania jako właściwości oznacza, że można go nadpisać, jeśli metoda jest wywoływana wiele razy, co prowadzi do zadania, które nigdy nie zostanie pomyślnie zakończone, dopóki system operacyjny nie zostanie zmuszony do zakończenia w momencie wygaśnięcia. .
Ten wzorzec wymaga unikalnej właściwości dla każdego wywołania,
beginBackgroundTaskWithExpirationHandler
co wydaje się kłopotliwe, jeśli masz większą aplikację z wieloma metodami sieciowymi.Aby rozwiązać te problemy, napisałem singletona, który zajmuje się wszystkimi hydraulikami i śledzi aktywne zadania w słowniku. Żadne właściwości nie są potrzebne do śledzenia identyfikatorów zadań. Wydaje się, że działa dobrze. Użycie jest uproszczone do:
Opcjonalnie, jeśli chcesz zapewnić blok ukończenia, który robi coś poza zakończeniem zadania (który jest wbudowany), możesz wywołać:
Odpowiedni kod źródłowy dostępny poniżej (dla zwięzłości wykluczono pojedyncze elementy). Komentarze / opinie mile widziane.
źródło
typedef
CompletionBlock? Po prostu to:typedef void (^CompletionBlock)();
Oto klasa Swift, która hermetyzuje uruchamianie zadania w tle:
Najprostszy sposób użycia:
Jeśli musisz poczekać na wywołanie zwrotne pełnomocnika przed zakończeniem, użyj czegoś takiego:
źródło
begin
metody, ale łatwo zobaczyć, jak dodać tę funkcję.Jak wspomniano tutaj i w odpowiedziach na inne pytania SO, NIE chcesz używać
beginBackgroundTask
tylko wtedy, gdy Twoja aplikacja przejdzie w tło; Przeciwnie, należy użyć zadania tła dla każdej operacji czasochłonnego którego ukończenie chcesz mieć pewność, nawet jeśli aplikacja ma iść w tle.Dlatego twój kod prawdopodobnie zostanie usiany powtórzeniami tego samego standardowego kodu do wywoływania
beginBackgroundTask
iendBackgroundTask
spójnie. Aby zapobiec takim powtórzeniom, z pewnością rozsądne jest zapakowanie schematu w jakąś pojedynczą zamkniętą całość.Podoba mi się niektóre z istniejących odpowiedzi za to, ale myślę, że najlepszym sposobem jest użycie podklasy Operation:
Możesz umieścić Operację w kolejce do dowolnej kolejki OperationQueue i manipulować tą kolejką według własnego uznania. Na przykład możesz przedwcześnie anulować wszelkie istniejące operacje w kolejce.
Jeśli masz więcej niż jedną rzecz do zrobienia, możesz połączyć wiele operacji zadań w tle. Operacje obsługują zależności.
Kolejka operacji może (i powinna) być kolejką w tle; w związku z tym nie ma potrzeby martwić się wykonywaniem kodu asynchronicznego wewnątrz zadania, ponieważ operacja jest kodem asynchronicznym. (Rzeczywiście, nie ma sensu wykonywanie innego poziomu kodu asynchronicznego wewnątrz Operacji, ponieważ Operacja zakończyłaby się, zanim ten kod mógłby się nawet rozpocząć. Gdybyś musiał to zrobić, użyłbyś innej Operacji.)
Oto możliwa podklasa Operation:
Powinno być oczywiste, jak tego użyć, ale jeśli tak nie jest, wyobraź sobie, że mamy globalną OperationQueue:
Tak więc w przypadku typowej, czasochłonnej partii kodu powiedzielibyśmy:
Jeśli twoją czasochłonną partię kodu można podzielić na etapy, możesz chcieć się wycofać wcześniej, jeśli zadanie zostanie anulowane. W takim przypadku po prostu wróć przedwcześnie z zamknięcia. Zwróć uwagę, że odniesienie do zadania od wewnątrz musi być słabe, w przeciwnym razie otrzymasz cykl utrzymania. Oto sztuczna ilustracja:
W przypadku konieczności wyczyszczenia w przypadku przedwczesnego anulowania samego zadania w tle, zapewniam opcjonalną
cleanup
właściwość obsługi (nie używana w poprzednich przykładach). Niektóre inne odpowiedzi były krytykowane za nieuwzględnianie tego.źródło
Wdrożyłem rozwiązanie Joela. Oto pełny kod:
plik .h:
plik .m:
źródło