2011-08-16 9 views
6

Mam pytanie na temat bezpieczeństwa gwintu poniższym przykładzie kodu firmy Apple (z przewodnikiem programowania GameKit)Modyfikacja zmienny obiekt w przewodnika realizacji

To załadować osiągnięć z centrum gier i zapisać go lokalnie:

Krok 1) Dodaj zmienną właściwość słownika do klasy, która zgłasza osiągnięcia. Słownik ten przechowuje kolekcję obiektów osiągnięć.

@property(nonatomic, retain) NSMutableDictionary *achievementsDictionary; 

Krok 2) Zainicjuj słownik osiągnięć.

Krok 3) Zmodyfikuj kod ładujący dane o osiągnięciach, aby dodać obiekty osiągnięć do słownika.

{ 
    [GKAchievement loadAchievementsWithCompletionHandler:^(NSArray *achievements, NSError *error) 
     { 
      if (error == nil) 
      { 
       for (GKAchievement* achievement in achievements) 
        [achievementsDictionary setObject: achievement forKey: achievement.identifier]; 
      } 
     }]; 

Moje pytanie wygląda następująco - obiekt achievementmentsDictionary jest modyfikowany w module obsługi zakończenia, bez żadnych blokad sortowania. Czy jest to dozwolone, ponieważ moduły obsługi zakończenia są blokiem pracy, który będzie gwarantowany przez system iOS, który zostanie wykonany jako jednostka w głównym wątku? I nigdy nie napotkasz problemów związanych z bezpieczeństwem wątków?

W innej firmy Apple przykładowy kod (GKTapper), ta część jest obsługiwane w różny sposób:

@property (retain) NSMutableDictionary* earnedAchievementCache; // note this is atomic 

Następnie w Handler:

[GKAchievement loadAchievementsWithCompletionHandler: ^(NSArray *scores, NSError *error) 
     { 
      if(error == NULL) 
      { 
       NSMutableDictionary* tempCache= [NSMutableDictionary dictionaryWithCapacity: [scores count]]; 
       for (GKAchievement* score in scores) 
       { 
        [tempCache setObject: score forKey: score.identifier]; 
       } 
       self.earnedAchievementCache= tempCache; 
      } 
     }]; 

Więc dlaczego inny styl i jest jednym ze sposobów bardziej poprawne niż inne?

Odpowiedz

2

Czy jest to dozwolone, ponieważ moduły obsługi zakończenia są blokiem pracy, który będzie gwarantowany przez iOS, który zostanie wykonany jako jednostka w głównym wątku? I nigdy nie napotkasz problemów związanych z bezpieczeństwem wątków?

To zdecydowanie nie dotyczy. Dokumentacja dla -loadAchievementsWithCompletionHandler: jawnie ostrzega, że ​​procedura obsługi zakończenia może zostać wywołana w wątku innym niż ten, od którego rozpoczęto ładowanie.

"Przewodnik po wątkach programowania firmy Apple" klasyfikuje NSMutableDictionary wśród klas wątków niebezpiecznych, ale kwalifikuje to za pomocą: "W większości przypadków można używać tych klas z dowolnego wątku, o ile używa się ich tylko z jednego wątku na raz. "

Tak więc, jeśli obie aplikacje są zaprojektowane tak, że nic nie będzie działać z pamięcią podręczną osiągnięć, dopóki zadanie robota nie zakończy aktualizacji, nie będzie wymagana żadna synchronizacja. Jest to jedyny sposób, w jaki mogę zobaczyć pierwszy przykład, jako bezpieczny, i jest to niepewne bezpieczeństwo.

Ten drugi przykład wygląda tak, jakby polegał na obsłudze właściwości atomowych, aby utworzyć miksowanie pomiędzy starą pamięcią podręczną a nową pamięcią podręczną. To powinno być bezpieczne, pod warunkiem, że cały dostęp do nieruchomości odbywa się za pośrednictwem swoich akcesorów, a nie bezpośredniego dostępu do ivar. Dzieje się tak dlatego, że akcesory są zsynchronizowane względem siebie, więc nie ryzykujesz zobaczenia połowy wartości. Ponadto getter zachowuje i autorelease zwracaną wartość, dzięki czemu kod ze starszą wersją będzie mógł zakończyć pracę bez awarii, ponieważ został zwolniony w trakcie pracy. Nieatomowy pobierający po prostu zwraca obiekt bezpośrednio, co oznacza, że ​​można go zwolnić z kodu, jeśli nowa wartość została ustawiona dla tej właściwości przez inny wątek. Bezpośredni dostęp do ivar może napotkać ten sam problem.

Powiedziałbym, że ten drugi przykład jest poprawny i elegancki, choć może nieco zbyt subtelny, bez komentarza wyjaśniającego, jak istotna jest atomowość obiektu.

+0

dzięki Jeremy. Pytałem również o ogólnie obsługiwane programy obsługi zakończeń bloków. Jeśli dokumentacja nie mówi wprost, gdzie wywoływany jest moduł obsługi zakończenia, czy można bezpiecznie założyć, że nie jest on wywoływany w głównym wątku? Jeśli zostanie określone, aby zostać wywołanym z głównej kolejki, to bezpiecznie jest zrobić cokolwiek w środku, nie martwiąc się o bezpieczeństwo wątków (zakładając, że cały dostęp jest z głównego wątku lub procedury kompilacji zwanej główną kolejką). –

+0

Jeśli nie określa, że ​​twój blok zostanie wywołany w głównej kolejce/wątku, musisz podjąć własne środki, aby upewnić się, że tak jest, jeśli tego właśnie potrzebujesz. Może on zostać wywołany w głównym wątku, ale w tym momencie fakt ten jest szczegółem implementacji, a nie częścią umowy API i może ulec zmianie bez ostrzeżenia. Jeśli zostanie określone, aby zostać wywołanym z głównej kolejki, możesz w pełni to wykorzystać. –

+0

Jedno ostatnie pytanie - jeśli zmienię pierwszy program obsługi zakończenia, aby użyć funkcji dispatch_async (dispatch_get_main_queue(),^{dla (osiągnięcia GKAchievement * w osiągnięciach) [achievementmentsDictionary setObject: achievement forKey: achievement.identifier];});) będzie to Kod może być również bezpieczny dla wątków? Pamiętam, że przeczytałem coś o tym, że nie jest to bezpieczne. –