【问题标题】:iOS app "has active assertions beyond permitted time - occasional crashes"iOS 应用程序“有超出允许时间的活动断言 - 偶尔崩溃”
【发布时间】:2015-11-07 08:18:43
【问题描述】:

我的一些用户遇到了这个崩溃(据他们说,它发生在使用该应用程序 4-5 分钟后),但我自己无法重现它:

Application Specific Information:
<BKNewProcess: 0x175466c0; com.zsquare.iPadApp; pid: 5005; hostpid: -1> has active assertions beyond permitted time: 
{(
    <BKProcessAssertion: 0x17545c90> id: 48-3A424578-FF1D-4484-9026-B4C6A83AD7EF name: Background Content Fetching (191) process: <BKNewProcess: 0x175466c0; .com.zsquare.ijournalPad; pid: 5005; hostpid: -1> permittedBackgroundDuration: 30.000000 reason: backgroundContentFetching owner pid:48 preventSuspend  preventThrottleDownUI  preventIdleSleep  preventSuspendOnSleep 
)}

Elapsed total CPU time (seconds): 0.460 (user 0.460, system 0.000), 2% CPU 
Elapsed application CPU time (seconds): 0.013, 0% CPU

Filtered syslog: None found

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0:
0   libsystem_kernel.dylib          0x362a4ff0 mach_msg_trap + 20
1   libsystem_kernel.dylib          0x362a4df4 mach_msg + 38
2   CoreFoundation                  0x23fa58c4 __CFRunLoopServiceMachPort + 134
3   CoreFoundation                  0x23fa3c4c __CFRunLoopRun + 1034
4   CoreFoundation                  0x23ef7118 CFRunLoopRunSpecific + 518
5   CoreFoundation                  0x23ef6f04 CFRunLoopRunInMode + 106
6   GraphicsServices                0x2d081ac8 GSEventRunModal + 158
7   UIKit                           0x28139f14 UIApplicationMain + 142
8   SimpleList-iPad                 0x0000f116 main (main.m:17)
9   libdyld.dylib                   0x361e9872 start + 0

现在我查看了处理此崩溃的其他各种 SO 问题,但没有一个答案对我有帮助,所以我想我会在这里发布我自己的设置和代码。

首先,发生这种情况的功能与应在应用中运行的重复任务有关。为此,当应用程序首次启动时,我在应用程序中重复了一个计时器:

- (void) setupRepeatTimer {

self.repeatTimer = [NSTimer scheduledTimerWithTimeInterval: 60.0 target:self selector:@selector(checkForAutomaticTaskTime:) userInfo:nil repeats: YES];
self.repeatTimer.tolerance = 1.0;

}

- (void) checkForAutomaticTaskTime: (NSTimer *) timer {

   if (self.taskIdentifier) {
    [[UIApplication sharedApplication] endBackgroundTask: self.taskIdentifier];
   }

   self.taskIdentifier = UIBackgroundTaskInvalid;
   [self beginBackgroundUpdateTask];


   // simplified, but there are more conditional checks here
   if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive && setting_enabled_for_automatic_task) {
        [self startAutomaticBackup];
   } else {
        [self endBackgroundUpdateTask];
   }
}

需要运行的备份可以是可变大小的,具体取决于用户的数据。这是通用代码:

- (void) startAutomaticBackup {

    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        [self runBackupWithCompletionBlock:^(NSString * filename) {

            dispatch_async(dispatch_get_main_queue(), ^{
                // finish on the main thread
                [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

                [self endBackgroundUpdateTask];

            });
        }];
    });
}

可以肯定的是,beginBackgroundUpdateTaskendBackgroundUpdateTask 看起来与 SO 和 Apple 指南中的代码示例完全相同:

- (void) beginBackgroundUpdateTask
{
    self.taskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"CJAutoBackup" expirationHandler:^{
        NSLog(@"beginBackgroundTask about to end = %lu", (unsigned long)self.taskIdentifier);
        [self endBackgroundUpdateTask];
    }];
}

- (void) endBackgroundUpdateTask
{
    if (self.taskIdentifier) {
        [[UIApplication sharedApplication] endBackgroundTask: self.taskIdentifier];
        self.taskIdentifier = UIBackgroundTaskInvalid;
    }
}

====

这就是我的设置。从用户描述来看,在应用程序运行 4-5 分钟后,应用程序在前台出现此崩溃报告时似乎崩溃了。如果他们禁用自动备份设置,该应用程序将再次正常工作。

这里的主要混淆是当应用程序在前台而不是后台使用它时应用程序崩溃,但错误消息谈论的是后台任务断言。这怎么可能?

另外,我可以做些什么来防止这个问题?我已经看到提到 UIApplication 的 backgroundTimeRemaining 属性,但我不确定在我的示例中如何或在何处使用它。

使用 NSTimer 会导致任何并发症吗?在设备上,如果应用程序已经在后台,它似乎不会触发计时器。

感谢您的帮助。

【问题讨论】:

    标签: ios objective-c nstimer uiapplication uibackgroundtask


    【解决方案1】:

    这是一个线程问题。正如您所怀疑的那样,问题确实是开始/结束后台任务调用的分解和架构。您正在尝试将 one 后台任务标识符维护为 property,然后您每 60 秒重复更改一次,无论实际任务花费多长时间(在后台线程上) )。这种令人困惑的伪循环架构会导致您的后台任务与其标识符不同步,因此后台任务的时间到期而您没有使用适当的标识符调用endBackgroundTask

    您需要修改此架构,以便每个任务拥有一个后台任务标识符,从而使所有内容保持独立。我想您会发现最简单的方法是将每个自动备份表示为单独的 NSOperation。

    【讨论】:

    • 谢谢。计时器应该在任何现有标识符上调用endBackgroundTask,并在计时器开始时将其标记为无效,然后再开始另一个任务,那么这仍然是一个问题吗?再看一遍,在我看来问题可能是“备份”过程可能需要超过 60 秒,所以当它完成并调用完成块时,它正在对过期标识符调用 endBackgroundTask?这会产生相同的问题和堆栈跟踪,还是您认为某些标识符没有得到endBackgroundTask 仍然存在问题?
    • 我想我们说的是同一件事。看,我不知道你的代码做了什么,我认为你也不知道。但我们知道您只有一个标识符,但您有可能同时执行多个备份任务。这太疯狂了,即使 从未 发生过(听起来在您的测试中没有发生)。因此我的建议是:每个备份都是自己的 NSOperation,有自己的后台任务和自己的后台任务标识符。没有更多的“循环”!你的代码会更简单。
    • 我同意一般观点,我认为我看到了问题所在。解决方法是从 NSTimer 函数中取出 taskIdentifier,并且只调用它来包装实际的备份功能(还添加一个布尔值以防止在备份运行时调用备份代码)。这样,一次只有一个后台任务在进行。
    • 是的,这正是我一直在说的。一个标识符,一个任务,一个备份。但它们不必是连续的。一旦你将这个结构化为 NSOperation,如果需要,它们可以在一定程度上是并发的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-14
    相关资源
    最近更新 更多