2011-08-16 1 views
6

:완료 핸들러에서 변경 가능한 객체를 수정</p> <p>이 게임 센터에서 성과를로드하고 로컬로 저장하는 것입니다 내가 애플 (에서 GameKit 프로그래밍 가이드)에서 다음 코드 예제의 스레드 안전성에 대한 질문이

1) 업적을 신고하는 클래스에 변경 가능한 사전 속성을 추가합니다. 이 사전은 업적 개체 컬렉션을 저장합니다.

@property(nonatomic, retain) NSMutableDictionary *achievementsDictionary; 

2 단계) 업적 사전을 초기화합니다.

achievementsDictionary = [[NSMutableDictionary alloc] init]; 

3 단계)로드 업적 데이터를로드하는 코드를 수정하여 업적 개체를 사전에 추가합니다.

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

내 질문은 다음과 같습니다 .- achievementsDictionary 객체가 정렬 핸들을 잠그지 않고 완료 핸들러에서 수정 중입니다. 완료 핸들러는 iOS가 메인 스레드에서 단위로 실행되도록 보장되는 작업 블록이기 때문에 허용됩니까? 그리고 스레드 안전 문제를 다루지 않습니까? 핸들러에서 다음

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

을 : 다른 애플 샘플 코드 (GKTapper)에서

,이 부분은 다르게 처리됩니다

[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; 
      } 
     }]; 

왜 다른 스타일, 하나 개의 방법이 더 정확 다른 것보다?

답변

2

완료 핸들러는 iOS가 메인 스레드에서 단위로 실행되도록 보장되는 작업 블록이기 때문에 허용됩니까? 그리고 스레드 안전 문제를 다루지 않습니까?

여기에는 분명히 해당되지 않습니다. -loadAchievementsWithCompletionHandler:에 대한 문서에서는 완료 핸들러가로드를 시작한 스레드가 아닌 다른 스레드에서 호출 될 수 있다고 명시 적으로 경고합니다.

Apple의 "스레딩 프로그래밍 가이드"는 스레드가 안전하지 않은 클래스 중에서 NSMutableDictionary을 분류하지만 "대부분의 경우 한 번에 하나의 스레드에서만 사용할 수있는 한 모든 스레드에서 이러한 클래스를 사용할 수 있습니다. "

따라서 두 가지 앱이 작업자 작업이 업데이트를 완료 할 때까지 업적 캐시로 작업하지 않도록 설계된 경우 동기화가 필요하지 않습니다. 이것이 첫 번째 예가 안전하다는 것을 볼 수있는 유일한 방법이며 안전이 약합니다.

이전 예제는 이전 캐시와 새 캐시 사이의 switcheroo를 만들기 위해 원자 속성 지원에 의존하는 것처럼 보입니다. 이 속성에 대한 모든 액세스가 직접 ivar 액세스가 아닌 접근자를 통해 이루어지면 안전해야합니다. 접근자가 서로 동기화되어 있으므로 반 설정 값을 볼 위험이 없기 때문입니다. 또한 getter는 반환 값을 유지하고 자동 반환하므로 이전 버전의 코드는 작업 도중에 릴리스 되었기 때문에 충돌하지 않고 작업을 완료 할 수 있습니다. nonatomic 게터는 단순히 객체를 직접 반환하기 때문에 다른 스레드가 해당 속성에 새 값을 설정하면 코드 아래에서 할당을 해제 할 수 있습니다. 직접 ivar 액세스는 동일한 문제점을 일으킬 수 있습니다.

저는 후자의 예가 정확하고 우아하지만, 속성의 원 자성이 얼마나 중요한지를 설명하지 않고 조금 지나치게 미묘하다고 말합니다.

+0

Jeremy에게 감사드립니다. 나는 블록 완성 핸들러에 대해서도 전반적으로 질문했다. 문서에서 완성 처리기가 호출되는 위치를 명시 적으로 밝히지 않으면 기본 스레드에서 호출되지 않는다고 가정하는 것이 안전합니까? 메인 큐에서 호출하도록 지정되면 스레드 안전성에 대한 걱정없이 내부에서 작업을 수행하는 것이 안전합니다 (모든 액세스가 기본 스레드에서 호출되거나 complient 핸들러가 메인 큐에서 호출되었다고 가정). –

+0

블록이 메인 큐/스레드에서 호출되도록 지정하지 않은 경우 필요한 경우 사용자가 직접 조치를 취해야합니다. 실제로는 주 스레드에서 호출 될 수도 있지만 그 시점에서 API 계약의 일부가 아닌 구현 세부 사항이며 경고없이 변경 될 수 있습니다. 기본 대기열에서 호출되도록 지정하면 해당 작업을 최대한 활용할 수 있습니다. –

+0

마지막 질문 하나 - dispatch_async (dispatch_get_main_queue(),^{업적의 GKAchievement * 업적)에 대해 [achievementDictionary setObject : achievement forKey : achievement.identifier];});)를 사용하여 첫 번째 완료 핸들러를 변경하면 이됩니다. 코드가 threadsesafe 수 있습니까? 이것에 대해 무언가를 읽는 것이 안전하지 않다는 것을 기억합니다. –