0

두 개의 다른 dispatch queues을 동시에 실행하고 365 회 반복하고 객체를 인스 턴싱합니다. 이 루프는 객체를 NSNotificationCenter에 추가 한 다음 객체가 이벤트 저장소 비동기 코드 블록이 완료되면 알림을 게시합니다. 문제는 디버거에서 730 개의 메시지를 받아야한다는 것입니다.하지만 그렇지 않습니다. 앱을 실행할 때마다 513에서 630까지 다양하게 메시지가 수신됩니다.NSNotificationCenter 옵저버는 각각 다른 수의 알림을받습니다.

이러한 이유가 있을까요?

이것은 루프를 수행하고 Notification Center에 개체를 추가하는 코드입니다. 내가 불변의 배열에 추가 HZCalendarDay의 인스턴스를 만듭니다. 같은 클래스에서

- (id)init { 
    self = [super init]; 

    if (self) { 
     // Alloc/Init instance variables. 
     previousDays = [[NSMutableArray alloc] init]; 
     futureDays = [[NSMutableArray alloc] init]; 
     self.datesOnCalendar = [[NSMutableArray alloc] init]; 
     self.stateOfCalendarCache = StateOfEventStoreCache_CachingRequired; 
     self.currentDay = [[HZCalendarDay alloc] init]; 

     // Setup our event store security access. 
     [self setupEventStore]; 

     DEDateUtility *dateUtility = [[DEDateUtility alloc] init]; 
     NSDate *today = [dateUtility normalizedDateWithDate:[NSDate date]]; 
     HZCalendarDay *date = [[HZCalendarDay alloc] initOnDate:today withEventStore:self.eventStore]; 
     [self.datesOnCalendar addObject:date]; 
     self.currentDay = date; 

     // Before we start caching, we need to setup the KVO so we can compile the 
     // completed caches, since both previous and future days are cached separately. 


     // Start caching previous days. 
     dispatch_queue_t previousDaysCacheQueue = dispatch_queue_create("previousDaysCacheQueue", NULL); 
     dispatch_async(previousDaysCacheQueue,^{ 
      int numberOfDays = (HZ_NUMBER_OF_TOTAL_YEARS_TO_CACHE * 365)/2; 
      for (int count = 1; count < numberOfDays; count++) { 
       NSDate *previousDate = [dateUtility adjustDate:today byNumberOfDays:-count]; 
       HZCalendarDay *calendarDay = [[HZCalendarDay alloc] initOnDate:previousDate withEventStore:self.eventStore]; 
       [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(completeCaching:) name:HZ_KVO_CALENDAR_CACHE_NOTIFICATION object:calendarDay]; 

       @synchronized (self.datesOnCalendar) { 
        [self.datesOnCalendar insertObject:calendarDay atIndex:0]; 
       } 
      } 
     }); 

     // Start caching future days. 
     dispatch_queue_t futureDaysCacheQueue = dispatch_queue_create("futureDaysCacheQueue", NULL); 
     dispatch_async(futureDaysCacheQueue,^{ 
      int numberOfDays = (HZ_NUMBER_OF_TOTAL_YEARS_TO_CACHE * 365)/2; 
      for (int count = 1; count < numberOfDays; count++) { 
       NSDate *futureDate = [dateUtility adjustDate:today byNumberOfDays:count]; 
       HZCalendarDay *calendarDay = [[HZCalendarDay alloc] initOnDate:futureDate withEventStore:self.eventStore]; 
       [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(completeCaching:) name:HZ_KVO_CALENDAR_CACHE_NOTIFICATION object:calendarDay]; 

       @synchronized (self.datesOnCalendar) { 
        [self.datesOnCalendar addObject:calendarDay]; 
       } 
      }; 
     }); 
    } 

    return self; 
} 

, 나는 방법에 내 NSNotificationCenter 게시물 및 cacheStage NSLogs 현재 값의 세터 있습니다. 결국 730과 같아야하지만 결코 그렇게 높지는 않습니다.

- (void)setCachingStage:(NSInteger)cachingStage { 
    _cachingStage = cachingStage; 
    NSLog(@ 
      "Cache Stage: %d", _cachingStage); 

    if (_cachingStage == (HZ_NUMBER_OF_TOTAL_YEARS_TO_CACHE*365)) { 
     self.stateOfCalendarCache = StateOfEventStoreCache_CachingComplete; 
     NSLog(@"Caching completed."); 
    } 
} 

- (void)completeCaching:(NSNotification *)notification { 
    [[NSNotificationCenter defaultCenter] removeObserver:self name:HZ_KVO_CALENDAR_CACHE_NOTIFICATION object:(HZCalendarDay *)notification.object]; 
    self.cachingStage++; 
} 

이는 이벤트 및 알림의 캐시를 잡고 있음은 HZCalendarDay의 이니셜이다. 이벤트 및 알리미가 설정되면 PostsNotificationCenter 번으로 설정합니다.

- (void)setCacheStage:(NSInteger)cacheStage { 
    _cacheStage = cacheStage; 

    if (_cacheStage == 2) { 
     [[NSNotificationCenter defaultCenter] postNotificationName:HZ_KVO_CALENDAR_CACHE_NOTIFICATION object:self]; 
    } 
} 

- (id)initOnDate:(NSDate *)date withEventStore:(EKEventStore *)eventStore { 
    self = [super init]; 

    if (self) { 
     self.events = [[NSArray alloc] init]; 
     self.reminders = [[NSArray alloc] init]; 
     self.date = date; 
     self.eventStore = eventStore; 

     [self fetchAllEvents]; 
     [self fetchAllReminders]; 
    } 

    return self; 
} 

- (void)fetchAllEvents { 
    NSPredicate *fetchPredicateForEvents = [self.eventStore predicateForEventsWithStartDate:[self startTime] endDate:[self endTime] calendars:[self.eventStore calendarsForEntityType:EKEntityTypeEvent]]; 
    self.events = [self.eventStore eventsMatchingPredicate:fetchPredicateForEvents]; 

    // Don't store a nil array in the dictionary. 
    if (!self.events) { 
     self.events = [[NSArray alloc] init]; 
    } 

    self.cacheStage++; 
} 

- (void)fetchAllReminders { 
    NSPredicate *fetchPredicateForReminders = [self.eventStore predicateForIncompleteRemindersWithDueDateStarting:[self startTime] ending:[self endTime] calendars:[self.eventStore calendarsForEntityType:EKEntityTypeReminder]]; 
    [self.eventStore fetchRemindersMatchingPredicate:fetchPredicateForReminders completion:^(NSArray *reminders) { 
     @synchronized (self.reminders) { 
      self.reminders = reminders; 
     } 
     self.cacheStage++; 

    }]; 
} 

사람이 잘못 갈 수있는 것을 나에게 설명 할 수 있습니까? 이것은 다중 스레드와 관련이 있습니까? 그렇다면 객체를 인스턴스화하는 클래스가 객체가 이벤트 저장소 미리 알림을 캐시했음을 알리는 더 좋은 방법이 있습니까? 이 클래스는 내 UITableView에 대한 데이터 소스이며, 캐싱이 완료되었음을 내 데이터 소스에 알리기 위해 미리 알림을 캐싱해야합니다. 이렇게하면 캐싱이 진행되는 동안 UI에 "새로 고침"표시 또는로드 표시기가 표시됩니다.

+0

알림 센터로 calendarDay 객체를 nil 인수로 대체하면 알림 센터가 수천 개의 NSLog로 콘솔을 스팸합니다. NSNotificationCenter가 예상대로 작동하지 않는 이유가 확실하지 않습니다. –

답변

0

cachingStage 구현 방법은 스레드로부터 안전하지 않습니다. 이 작업을 수행하기 때문에 단순히 @synchronized에서 cachingStage/setCachingStage:의 구현을 감싸는 있습니다,이 경우에 충분하지 않습니다 :

NSInteger foo = self.cacheStage; foo++; self.cacheStage = foo; 

경우 : 실제로하는 것과 동일

self.cachingStage++; 

... 당신이 두 개의 스레드 & B이 작업을 수행, 다음이 발생할 수 있습니다 :

  1. 스레드 A는현재 값을 읽고, foo.
  2. 스레드 A는 선매되어 잠자기 상태가됩니다.
  3. 스레드 B는 foo에 현재 값, x을 읽고 x + 1-foo를 증가하고, cachingStage에 다시 기록, 다른 물건의 무리를 수행, 결국 선취이다.
  4. 스레드 A가 재개되고 스레드가 다시 시작되면 foo에서 x + 1까지 증가하고 해당 값을 cachingStage에 다시 씁니다.

이 시점에서 cachingStagex + 2이 될 것으로 예상하지만 실제로는 그렇지 않습니다.x + 1입니다. 당신이 그것에 대해 생각한다면, 당신의 가치가 당신이 생각하는만큼 높지 않은 이유를 완벽하게 설명합니다. 여러 스레드에서 조작 할 수있는 모든 데이터/상태를 명시 적으로 보호해야합니다. 대신 setCachingStage:의,이 경우에, 당신은 아마 당신이 다음과 같이 구현할 수 incrementCachingStage 같은 방법 원하는 :

- (void) incrementCachingStage { 
    @synchronized(self) { 
     _cachingStage++; 
     NSLog(@"Cache Stage: %d", _cachingStage); 

     if (_cachingStage == (HZ_NUMBER_OF_TOTAL_YEARS_TO_CACHE*365)) { 
      self.stateOfCalendarCache = StateOfEventStoreCache_CachingComplete; 
      NSLog(@"Caching completed."); 
     } 
    } 
} 

하지만, 데이터의 한 조각, 그리고 여기에 잔뜩있어, 일부/모두 스레드로부터 안전하게 할 필요가 있습니다.