2015-05-13 2 views
2

У меня есть этот код, который пытается сделать фоновый выбор для данных HealthKit. Код работает нормально, когда я впервые запускаю приложение, но если я вручную выполняю фоновый выбор (используя команду debug), я получаю исключение и ошибку, которая говорит reason: 'this request has been neutered - you can't call -sendResponse: twice nor after encoding it', и я не совсем уверен, почему.NSInternalInconsistencyException при запуске фоновой выборки с помощью цикла

Вот код, который извлекает данные:

- (void)fetchNewDataWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { 

    if (!self.healthStore) { 
     self.healthStore = [HKHealthStore new]; 
    } 

    dataTypes = [NSDictionary dictionaryWithObjectsAndKeys: 
       [NSNumber numberWithInt:1], HKQuantityTypeIdentifierStepCount, 
       [NSNumber numberWithInt:2], HKQuantityTypeIdentifierFlightsClimbed, 
       [NSNumber numberWithInt:3], HKQuantityTypeIdentifierDistanceWalkingRunning, 
       [NSNumber numberWithInt:4], HKQuantityTypeIdentifierDistanceCycling, nil]; 
    achievementData = [NSMutableDictionary new]; 

    NSCalendar *calendar = [NSCalendar currentCalendar]; 
    NSDate *startDate = [calendar startOfDayForDate:[NSDate date]]; 
    NSDate *endDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:startDate options:0]; 
    NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionNone]; 

    for (NSString *key in dataTypes) { 

     HKSampleType *sampleType = [HKSampleType quantityTypeForIdentifier:key]; 

     HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:sampleType predicate:predicate limit:HKObjectQueryNoLimit sortDescriptors:nil resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) { 

      if (!error) { 

       if (!results) { 

        NSLog(@"No results were returned form query"); 

        completionHandler(UIBackgroundFetchResultNoData); 

       } else { 

        dispatch_async(dispatch_get_main_queue(), ^{ 

         [self processNewDataWithResults:results andType:key]; 

         completionHandler(UIBackgroundFetchResultNewData); 

        }); 

       } 

      } else { 

       NSLog(@"Error: %@ %@", error, [error userInfo]); 

       completionHandler(UIBackgroundFetchResultFailed); 

      } 

     }]; 

     [self.healthStore executeQuery:query]; 

    } 

} 

Тогда у меня есть отдельная функция, которая обрабатывает данные, которые вы можете увидеть вызывается, когда найдены результаты. Если вы хотите, я могу вставить его здесь, но он довольно длинный, не уверен, что он имеет какое-либо отношение к нему.

Я попытался поставить точки останова, чтобы увидеть, когда вызван обработчик завершения, но из того, что я могу сказать, это вызов только один раз, если только мне не хватает чего-то глупого здесь.

Если у кого-нибудь есть какие-либо советы, пожалуйста, дайте мне знать :) Спасибо!

EDIT Вот что сообщение об ошибке выглядит следующим образом:

2015-05-13 10:11:54.467 appName[379:169163] *** Assertion failure in -[UIFetchContentInBackgroundAction sendResponse:], /SourceCache/BaseBoard/BaseBoard-98.3/BaseBoard/BSAction.m:221 
2015-05-13 10:11:54.470 appName[379:169163] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'this request has been neutered - you can't call -sendResponse: twice nor after encoding it' 
*** First throw call stack: 
(0x28ed4137 0x36e30c77 0x28ed400d 0x29bd2bc9 0x2dbde865 0x397ed5 0x2dbde7cf 0x2ca82a3d 0x39019b 0x390187 0x393e9d 0x28e99889 0x28e97fa9 0x28de39a1 0x28de37b3 0x305951a9 0x2c56e695 0xdff29 0x373d8aaf) 
libc++abi.dylib: terminating with uncaught exception of type NSException 

ответ

5

Глядя на код, который вы в курсе, я не вижу никакого другого пути, чем фон выборки обработчика завершения будет называться именно 4 раза из-за петли for.

Дорожки код в resultHandlers каждого экземпляра HKSampleQuery будет в конечном итоге на completionHandler(UIBackgroundFetchResult...) вызова в любом случае, и вы всегда инстанцировании четыре из них, так это то, что утверждение 'you can't call -sendResponse: twice' жалуется ИМО.

Должно быть легко проверить, является ли это проблемой, комментируя 3 запроса из словаря dataTypes.

Edit: в соответствии с просьбой в комментариях, здесь возможное решение (..Just с верхней части моей головы, так возьмите его с зерном соли ..):

It (а) использует блокировка семафора, чтобы включить обратный вызов результата асинхронного запроса в синхронный вызов & (b) использует группу отправки, чтобы дождаться завершения всех 4 запросов перед выполнением обработчика завершения.

// NOTE: This example assumes that the fetch has "new data" if any of the queries returned something 
//  Also it skips the 'NSError' part (which could work exactly like the 'result' var) 

// Define one or more block-global result variables for queries can put their end state into 
UIBackgroundFetchResult __block result = UIBackgroundFetchResultNoData; 

// Create a dispatch group that will manage all the concurrent queries 
dispatch_queue_t queue = dispatch_queue_create([@"my.query.queue" UTF8String], DISPATCH_QUEUE_CONCURRENT); 
dispatch_group_t queries = dispatch_group_create(); 

// For each dataType dispatch a group block containing the query to run on the concurrent queue 
for (NSString *key in dataTypes) { 
    dispatch_group_async(queries, queue, ^{ 
     // To work around the asynchronous callback, I'll use a lock to wait for the query to return their result, so.. 

     // ..like above, a block-global var will hold the result of the query 
     BOOL __block success = NO; 

     // ..create a one-time lock.. 
     dispatch_semaphore_t lock = dispatch_semaphore_create(0); 
     HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:sampleType predicate:predicate limit:HKObjectQueryNoLimit sortDescriptors:nil resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) { 
      success = YES; // ..or however you define success.. ;) 

      dispatch_semaphore_signal(lock); // ..open lock to signal result available.. 
     }]; 
     dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); // ..wait for callback result lock to open.. 

     // ..determine & set result. 
     if (success) { 
      result = UIBackgroundFetchResultNewData; 
     } 
    }); 
} 

// Schedule a final block to execute (on the main queue) when all the other group blocks have finished running 
dispatch_group_notify(queries, dispatch_get_main_queue(), ^{ 
    // Determine final result and call completion handler 
    completionHandler(result); 
}); 
+0

Вы знаете, есть ли способ, по которому я могу вывести обработчик завершения из цикла или, возможно, получить данные по-другому? Благодаря! –

+0

В основном вам нужен способ запускать запросы, получать их соответствующие результаты и вызывать обработчик завершения один раз после * все * результаты возвращаются. Существует несколько способов сделать это, например, используя NSOperationQueues или GCD. Я исправлю ответ с помощью некоторого кода, основанного на группах отправки, которые должны указывать вам в правильном направлении. – mvanallen

+0

Это замечательный материал, я не слишком много перепутал с GCD, за исключением того, что я нашел для руководства по запросам Healthkit. Я сделаю это, я должен буду больше исследовать GCD и как все это входит, чтобы поиграть, чтобы узнать больше об этом. Еще раз спасибо! –