可能的对象释放错误,后台应用崩溃block_invoke_5

时间:2017-02-15 21:23:03

标签: ios objective-c watchkit health-kit

我的应用程序崩溃时出现意外问题,我特意努力调试,因为它发生在iOS系统确定的后台。我对代码有一些大写的注释,显示问题的回溯位置。我希望这很清楚。

我认为它与对象释放有关。

  • 我在初始化对象之前尝试过使用__block但是 这没有帮助。
  • 我也试过调度代码行 错误的主队列,但没有帮助。

实际崩溃列为AppName:__66- [BackgroundUpdateController initiateBackgroundHealthkitObservers] _block_invoke_5 + 160

如果某些代码不符合标准格式和约定,我会道歉。我是从各种各样的地方自学,所以没有适当的代码格式经验。

非常感谢

#import "BackgroundUpdateController.h"
NSUserDefaults *backgroundDefaults;
@implementation BackgroundUpdateController

-(id)init{
backgroundDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.HeartAnalyzer"];
return [super init];
}

-(void)initiateBackgroundHealthkitObservers{
// Check we should be running here
if(([backgroundDefaults integerForKey:@"sleepAnalysisEnabled"] != 1) || (![backgroundDefaults boolForKey:@"AutomaticSleepAdd"])) return;
// Initiate some variables, Use __block to ensure the backgroundHealthStore object does not get deallocated
__block HKHealthStore *backgroundHealthStore = [[HKHealthStore alloc] init];
HKQuantityType *activeEnergy = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierActiveEnergyBurned];
// Enable background delivery of active energy data from HealthKit
[backgroundHealthStore enableBackgroundDeliveryForType:activeEnergy frequency:HKUpdateFrequencyHourly withCompletion:^(BOOL success, NSError *error) {
}];
// Now setup an HKOberverQuery which triggers hourly if there are new active energy data points in HealthKit
HKObserverQuery *query = [[HKObserverQuery alloc] initWithSampleType:activeEnergy predicate:nil updateHandler:^(HKObserverQuery *query, HKObserverQueryCompletionHandler completionHandler, NSError *error) {
    UIApplicationState state = [[UIApplication sharedApplication] applicationState];
    if (state == UIApplicationStateBackground || state == UIApplicationStateInactive){// Only run when app is not in foreground
        // Load some more variables with checks to ensure they are valid objects
        NSDate *previousSavedDate = [backgroundDefaults objectForKey:@"DateBackgroundSleepLastSaved"];
        if(previousSavedDate == nil) previousSavedDate = [NSDate distantPast];
        NSDate *lastSleepCheck = [backgroundDefaults objectForKey:@"LastSleepCheck"];
        if(lastSleepCheck == nil) lastSleepCheck = [NSDate distantPast];
        // If the last save date was long enough ago and the last sleep check was long enough ago, proceed
        if(([previousSavedDate timeIntervalSinceNow] < -(3600*18)) && ([lastSleepCheck timeIntervalSinceNow] < -(3600*2))){
            [backgroundDefaults setObject:[NSDate date] forKey:@"LastSleepCheck"];
            [backgroundDefaults setBool:NO forKey:@"BackgroundSleepFound"];
            SleepTimesCalculator *sleepClass = [[SleepTimesCalculator alloc] init];
            [sleepClass calculateSleepTimes:^{
                NSLog(@"Background sleep time calculations complete");
                if([backgroundDefaults boolForKey:@"BackgroundSleepFound"]){// Only continue is a sleep time was found
                    __block NSMutableArray *savedSleepObjects = [backgroundDefaults valueForKey:@"SleepTimesDataBase"];
                    if(savedSleepObjects.count > 0){
                            __block NSMutableDictionary *sleepObject = [savedSleepObjects objectAtIndex:0]; // THE __BLOCK USED TO PREVENT THE OBJECT BEING DEALLOCATED, STILL SEEMS TO BE BASED ON THE CRASH
                            NSDate *sleepStart = [NSDate dateWithTimeIntervalSinceReferenceDate:[[sleepObject valueForKey:@"CalculatedSleepTime"]integerValue]];// Get the sleep time start date object
                            NSDate *sleepEnd = [NSDate dateWithTimeIntervalSinceReferenceDate:[[sleepObject valueForKey:@"CalculatedWakeTime"]integerValue]];
                            NSInteger sleepSavedToHealth = [[sleepObject valueForKey:@"SavedToHealth"] integerValue];// Check its not already been saved by some other element of the app
                            if(sleepSavedToHealth != 1){
                                HKCategorySample *sleepSample = [HKCategorySample categorySampleWithType:[HKCategoryType categoryTypeForIdentifier:HKCategoryTypeIdentifierSleepAnalysis] value:1 startDate:sleepStart endDate:sleepEnd];// Generate sleep object for HealthKit
                                [backgroundHealthStore saveObject:sleepSample withCompletion:^(BOOL success, NSError *error) {
                                    if (!success) NSLog(@"Uncommon Error! saveObject:sleepSample");
                                    else{
                                        dispatch_async(dispatch_get_main_queue(), ^{// DISPATCH TO MAIN QUEUE AN ATTEMPTED FIX FOR CRASH
                                            sleepObject = [savedSleepObjects objectAtIndex:0];// Choose the most recent sleep time to save
                                            [sleepObject setValue:[NSNumber numberWithInteger:1] forKey:@"SavedToHealth"];// THIS IS WHERE THE 'Last Exception Backtrace (0)' ENDS UP
                                            [savedSleepObjects replaceObjectAtIndex:0 withObject:sleepObject];// Replace the object which now has the 'Saved' tag
                                            [backgroundDefaults setObject:[NSDate date] forKey:@"DateBackgroundSleepLastSaved"];// Save the data of the last time we reached this point
                                            [backgroundDefaults setObject:savedSleepObjects forKey:@"SleepTimesDataBase"];// Save the sleep times back to the database
                                        });
                                    }
                                }];
                            }
                        completionHandler();// Call the completion handler as we've been throught the sleepObjects array
                    }
                    else completionHandler();// Call the completion handler anyway
                }
                else completionHandler();// Call the completion handler anyway
            }];

        }
        else completionHandler();
    }
}];
[backgroundHealthStore executeQuery:query];// Execute the HealthKit healthstore query
}

@end

1 个答案:

答案 0 :(得分:0)

前缀__block确实保证@"CalculatedSleepTime"

中存在sleepObject键的对象

我认为你误解了__block的工作原理。 This将是一个很好的指南。

在对代码的快速概述中,似乎[sleepObject valueForKey:@"CalculatedSleepTime"]正在返回nil&amp;如果没有可空性检查,您将尝试提取integerValue

所以,请考虑:

NSMutableDictionary *sleepObject = [savedSleepObjects objectAtIndex:0];
id calculatedSleepTime = [sleepObject valueForKey:@"CalculatedSleepTime"];

if(calculatedSleepTime){
    NSDate *sleepStart = [NSDate dateWithTimeIntervalSinceReferenceDate:[calculatedSleepTime integerValue]];
}

您似乎也不需要__block

中的HKHealthStore *backgroundHealthStore = [[HKHealthStore alloc] init];前缀