使用循环运行后台获取时出现 NSInternalInconsistencyException

Posted

技术标签:

【中文标题】使用循环运行后台获取时出现 NSInternalInconsistencyException【英文标题】:NSInternalInconsistencyException when running background fetch with a loop 【发布时间】:2015-05-13 14:06:40 【问题描述】:

我有这段代码正在尝试对 HealthKit 数据进行后台提取。当我第一次运行该应用程序时,代码运行良好,但如果我手动执行后台提取(使用调试命令),我会抛出一个异常和一个错误,上面写着reason: 'this request has been neutered - you can't call -sendResponse: twice nor after encoding it',我不太清楚为什么。

这是获取数据的代码:

- (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

【问题讨论】:

【参考方案1】:

通过查看您发布的代码,除了for 循环,我没有看到后台获取完成处理程序将被准确调用 4 次。

HKSampleQuery 的每个实例的 resultHandlers 中的代码路径在任何情况下都会以 completionHandler(UIBackgroundFetchResult...) 调用结束,并且您总是实例化其中的四个,所以这就是断言 'you can't call -sendResponse: twice' 所抱怨的关于 IMO。

通过注释掉dataTypes字典中的3个查询应该很容易检查这是否是问题。

编辑:根据 cmets 的要求,这是一个可能的解决方案(.. 只是在我的头上,所以带着一粒盐..):

它 (a) 使用信号量锁将异步查询结果回调转换为“同步”调用 & (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);
);

【讨论】:

你知道是否有一种方法可以让完成处理程序脱离循环,或者以不同的方式获取数据?谢谢! 基本上,您需要一种方法来运行查询、获取它们各自的结果并在所有结果返回后调用完成处理程序。有几种方法可以做到这一点,例如使用 NSOperationQueues 或 GCD。我将使用一些基于调度组的代码来修改答案,这些代码应该为您指明正确的方向。 这是很棒的东西,除了我在 Healthkit 查询教程中找到的内容外,我并没有过多地使用 GCD。我会试一试,我将不得不对 GCD 做更多的研究,以及这一切是如何发挥作用的,以了解更多关于它的信息。再次感谢! 这成功了,非常感谢,我不再收到错误了。

以上是关于使用循环运行后台获取时出现 NSInternalInconsistencyException的主要内容,如果未能解决你的问题,请参考以下文章

使用变量运行循环时出现问题

使用循环将信息流传递给setter时出现运行时错误

跟后台打印程序系统服务通讯时出现错误。请打开服务管理单元,确认后台打印程序服务是否在运行。

从后台运行应用程序时重新启动应用程序时出现闪屏问题

在使用 matplotlib 和 PySide2 运行的应用程序中使用 pdb 调试器时出现“事件循环已在运行”

循环运行时出现 Java 逻辑错误:If/else 计数器计数不正确