2015-05-13 45 views
2

我有這段代碼試圖爲HealthKit數據做後臺提取。當我第一次運行應用程序時,代碼工作正常,但如果我手動執行後臺提取(使用調試命令),我會得到一個異常拋出,並說錯誤,說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]; 

    } 

} 

然後,我有一個單獨的函數處理,你可以看到結果時,發現被調用的數據。如果你想我可以把它粘貼在這裏,但它很長,不知道它是否與它有任何關係。

我已經嘗試把斷點放在看看什麼時候完成處理程序被調用,但從我可以告訴它只被調用一次,除非我在這裏丟失了一些愚蠢的東西。

如果有人有任何建議,請讓我知道:)謝謝!

編輯 以下是錯誤消息是什麼樣子:

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

從看着你發佈的代碼,我沒有看到比背景提取完成處理任何其他方式將被準確地稱爲由於for循環而導致4次。

HKSampleQuery每個實例的resultHandlers的代碼路徑將在任何情況下completionHandler(UIBackgroundFetchResult...)通話結束了,你總是實例他們四人,所以這是斷言'you can't call -sendResponse: twice'抱怨IMO。

通過註釋dataTypes字典中的3個查詢,應該很容易檢查這是否是問題。

編輯:作爲意見要求,這裏有一個可能的解決方案(..just了我的頭頂,所以把它與鹽糧..):

它(一)使用將異步查詢結果回調變爲「同步」調用的信號量鎖(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

基本上,您需要一種方法來運行查詢,獲取它們各自的結果,並在* all *結果返回後調用完成處理程序一次。有幾種方法可以做到這一點,例如使用NSOperationQueues或GCD。我會根據調度組的一些代碼修改答案,這些代碼應該指向正確的方向。 – mvanallen

+0

這是很棒的東西,除了我在Healthkit查詢教程中找到的東西外,我沒有對GCD搞過太多東西。我會給出這個答案,我將不得不對GCD進行更多的研究,以及如何進一步瞭解它。再次感謝! –