2013-08-29 7 views
6

Wiele osób pytało pytanie o podobnym tytule, ale bardzo różne przeznaczenie:dispatch_get_current_queue() przestarzałe, czy istnieje alternatywa dla bezpiecznego CoreData?

CoreData wymaga że śledzenie bieżącej kolejki bieżącego wątku i aktualnej NSOperationQueue (jeśli jesteś NSOperation) , jeśli zezwalasz na wywołania metod pochodzące z innych klas (które domyślnie każda klasa pozwala). Nie ma żadnych "maybes" na ten temat: jest to trudne wymaganie.

To dobrze, i ogólnie jest to łatwe, aby zapewnić:

NSAssert([NSThread currentThread].isMainThread || myPrivateQueue == dispatch_get_current_queue(), @"You tried to call this method from an external thread, or a queue other than my internal private queue. That's not legal, and will cause data corruption"); 

... chyba, że ​​Apple ma i przestarzałej dispatch_get_current_queue(), podobno „, bo ludzie nadużywają go obejść brakuje funkcjonalność w GCD/bitów GCD, których nie rozumieli ".

Uwaga: moje użycie metody dispatch_get_current_queue() powyżej wydaje się być poprawne i nie jest obraźliwe, sądząc po komentarzu do nagłówka Apple: chodzi o to, że sprawdzam, czy kolejka jest prywatną, którą utworzyłem (co twierdzi Apple). jest dopuszczalnym użytkowaniem).

Pomijając mądrość wycofania czegoś po prostu ze względu na błędy w jego implementacji :(... ktoś znalazł sposób obejścia tego problemu przez Apple, w szczególności: z CoreData, musisz śledzić kolejkę - czy jest inny sposób to zrobić:

(ma to znaczenie, ponieważ: w przypadku CoreData, jeśli pozwolisz, aby coś przypadkowo wywołało taką metodę, nie dostaniesz "awarii", dostaniesz "dane zepsute, które pojawią się na . pewnym momencie w przyszłości, kiedy jest już za późno, by to naprawić ")

+0

Używam 'dispatch_queue_set_specific()' i 'dispatch_get_specific()' dla aplikacji, o którą pytałem tutaj: http://stackoverflow.com/questions/12806506/how-can-i-verify-that- i-am-running-on-a-given-gcd-queue-without-using-dispatch-g i dobrze działa, aby zidentyfikować określone kolejki. Komentarz Jody na temat odpowiedzi wspomina użycie czegoś podobnego w Core Data. –

+0

@BradLarson dispatch_get_specific() jest bardzo interesujący, dzięki. Nie jestem pewien, co się stanie, jeśli zadzwonisz, gdy nie będziesz w bloku (w prawidłowym wykonaniu, byłbyś, ale chciałbym wiedzieć, co stanie się, gdy coś pójdzie nie tak, ponieważ to jest konfiguracja, którą próbuję złapać/potwierdzić/zalogować się) – Adam

+0

Nie wiem, czy bycie w bloku ma znaczenie. Wszystko, co testuje, to klucz przypisany do konkretnej kolejki, na której działa ten kod. Jak pokazuję, używam tego dla funkcji, która gwarantuje synchroniczne wykonanie kodu w konkretnej kolejce, albo przez synchroniczną wysyłkę bloku do kolejki, albo przez uruchomienie kodu bezpośrednio, jeśli już jest w tej kolejce. –

Odpowiedz

4

performBlock zawsze uruchomić go w prawidłowym wątku Wystarczy użyć:

[yourManagedObjectContext performBlock:^{ 
//do your stuff here 
}]; 

Nie potrzebujesz już bieżącego kontekstu bieżącego, ponieważ twój MOC wie, który wątek podpala ten MOC na pierwszym miejscu. Podczas korzystania z performBlock lub performBlockAndWait jesteś bezpieczny.

+0

Dobry pomysł, ale wydaje się, że ma on problemy z blokowaniem wątków i wydajnością w złożonych sytuacjach - przez "problemy" rozumiem "może problemy z CoreData, prawdopodobnie także błędy w naszym kodzie, być może oba razem". Jednym z moich pierwszych kroków było rozpoczęcie głośnego twierdzenia, aby dowiedzieć się, dlaczego zdarzają się przypadki "CoreData zawiesza się na dziesiątki sekund na muteksie, który wydaje się niekwestionowany". – Adam

+0

... więc próbuję usunąć naszą zależność od "always performBlock na wszystko" i dowiedzieć się, co się właściwie dzieje/nie dzieje się – Adam

+0

"Always' performBlock: 'for everything" jest praktycznie zalecanym sposobem na wykonanie AFAICT. 'dispatch_get_current_queue()' jest w dużej mierze bezużyteczne ze względu na sposób, w jaki działa libdispatch - istnieje niewielka liczba "globalnych" kolejek i innych kolejek, które docelowo wykonują operacje w tych kolejkach. Czy to wywołanie jest bezużyteczne, ponieważ jest bieżącą kolejką? Ten, który został zaklasyfikowany, czy też kolejka globalna, na której działa, a enqueuer jest arbitrem tego, w której kolejce się znajdujesz, w tym przypadku 'performBlock:' jest enqueuer i jest to jedyny sposób, aby wiedzieć, że jesteś w tej kolejce. – ipmcc

0

W moim konkretnym przypadku Wymieniłem wszystkie połączenia performBlock według:

  1. poruszające wszystkie działania CoreData do jednej klasy
  2. tworząc prywatną dispatch_queue
  3. łączących wszystkie moje wewnętrzne połączenia w jeden zadzwoń:
  4. pojedyncza rozmowa sama się zawiązuje w dispatch_async ((prywatna wewnętrzna kolejka))
    1. ... i pierwsza rzecz, którą robi wewnątrz dispatc h to "if (self.privateMOC == zero)" ...i tworzy lokalną MOC, gwarantując, że MOC tylko zawsze jest dostępny i creatd na tej jednej kolejce

się dwie rzeczy:

  1. mutex/zawiesza lock wszyscy zniknęły (choć może to być zbieg okoliczności - poinformuję później, jeśli okaże się, że blokady są nadal blokowane). Wydajność wydaje się zauważalnie lepsza niż w przypadku performBlock: wszędzie jest używana
    1. UWAGA: mieliśmy wiele wywołań performBlock i robiliśmy to często (wrażliwe dane, które wymagają zmiany zawartości MOC CoreData przy umiarkowanie wysokiej częstotliwości). Nie wiem, czy byłoby zauważalnej różnicy w normalnej aplikacji

... tak, na razie, do moich celów: to okazało się dobrym rozwiązaniem. Nie sądzę, że jest to "właściwa" ogólna odpowiedź na to pytanie, ale ... polecam ją każdemu, kto uzna, że ​​"performBlock" jest mniej panaceum, niż się wydaje.

Powiązane problemy