如何运行一个非主队列处于后台状态?

How to run a non main queue in a background state?

请看一下这段非常简单的代码:

dispatch_async(dispatch_get_main_queue(), ^{
   
    for (int i = 0; i < 100; i++)
    {
        NSLog(@"LOOP %d", i);
        sleep(1);
    }
});

如果我将我的应用程序发送到后台状态,它仍然是 运行ning。但是,如果我像这样将执行放到非主队列中:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    
    for (int i = 0; i < 100; i++)
    {
        NSLog(@"LOOP %d", i);
        sleep(1);
    }
});

然后当我的应用程序进入后台时暂停执行。在非主队列上调度时是否可以使其 运行 处于后台状态?

我需要说明一下,我正在 运行我的应用程序启用了这些后台模式:

<key>UIBackgroundModes</key>
<array>
    <string>bluetooth-central</string>
    <string>voip</string>
</array>

请尝试使用 NSProcessInfo performExpiringActivityWithReason API

[[NSProcessInfo processInfo] performExpiringActivityWithReason:@"myReason" usingBlock:^(BOOL expired)
{
  // This block is run on a separate (background) thread
  // Put your code here...
}

请注意,这只是请求在后台运行的进程上额外 CPU 时间...您不能无限期地 运行 后台代码。

当你即将被杀死时,expired == YES将第二次调用该块。

除了使用performExpiringActivityWithReason,您还可以使用UIBackgroundTaskIdentifier API:

// Perform the task on a background queue.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
   // Request the task assertion and save the ID.
   self.backgroundTaskID = [[UIApplication sharedApplication]
              beginBackgroundTaskWithName: @"Finish Pending Tasks" expirationHandler:^{
       // End the task if time expires.
       [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskID];
       self.backgroundTaskID = UIBackgroundTaskInvalid;
   }];
        
   // Add your code here.
        
   // End the task assertion.
   [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskID];
   self.backgroundTaskID = UIBackgroundTaskInvalid;
};

到期处理程序是在应用程序被终止之前调用的,但不要计划使用此方法执行计算量大的任务,因为 OS 具有系统范围的时间限制,这超出了开发人员的范围控制。

如果您需要在后台为某些系统事件(如网络获取)执行特定任务,请考虑使用后台模式。

在这种情况下,使用 UIKit API 的优势是您可以在 for 循环中查询 [[UIApplication sharedApplication] backgroundTimeRemaining] 以执行任何最后一分钟的清理步骤。

请注意,如果您使用的是 App Extensions,Apple 建议使用 NSProcess API,因此建议会因用例而异。