Search code examples
iosobjective-cuikitgrand-central-dispatchbackground-process

Understanding background task execution syntax and GCD


I'm trying to fully understand the below code I put together after researching background tasks in iOS and am hoping for some help,

I understand the basic concept,

First we get the app singleton, then we create a block and register with the system the background task, and then finally we asynchronously dispatch the task to run.

So here are the pieces I'm looking for help with:

  1. When background_task is assigned the block, the actual block does not have the code we want run inside it, only the cleanup code in it's completion handler, why is that?

  2. I understand dispatch_async basically starts a new thread and starts working through the code in the block, but where in this dispatch_async request is the background_task referenced? I don't see how the system understands that the code we want executed in the dispatch_async request is related to the background_task we registered earlier.

  3. Why do we need the cleanup code both at the end of the dispatch_async block and in the completion handler of the background_task?

Sorry if these are stupid questions, but I just don't get the syntax,

Here is the code i've cobbled together:

UIApplication *application = [UIApplication sharedApplication]; //Get the shared application instance

__block UIBackgroundTaskIdentifier background_task; //Create a task object

background_task = [application beginBackgroundTaskWithExpirationHandler: ^ { //Register background_task    
                [application endBackgroundTask: background_task]; //Tell the system that we are done with the tasks
                background_task = UIBackgroundTaskInvalid; //Set the task to be invalid
                //Above code called when endBackgroundTask is called
            }];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                //Perform your tasks that your application requires

                [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(updateText) userInfo:nil repeats:YES];
                NSLog(@"\n\nRunning in the background!\n\n");

                [application endBackgroundTask: background_task]; //End the task so the system knows that you are done with what you need to perform
                background_task = UIBackgroundTaskInvalid; //Invalidate the background_task
            });

Solution

  • There is no relationship between the background task identifier and the work you do on a secondary thread. The background task represents a request for some extra time to run. That's all. You need to end it to tell the OS that you've completed the work you wanted to do. If you fail to do that in the time available, your app will be terminated. The task ID is just a token representing the permission from the OS to keep working for a bit of time.

    You have to clean up in both places, the expiration handler and the end of your async-dispatched block, because they represent two different occurrences. In the block you dispatch to the concurrent queue, you end the task because you've completed your work in time and you want to let the OS know so it can suspend your app; it doesn't need to terminate it. In the expiration handler, that's your last chance to end the task to prevent your app from being terminated. You haven't completed your work, but you've run out of time. If you didn't end the background task at that point, the OS would kill your app.

    By the way, scheduling a timer in a task running on a dispatch queue won't work. A timer is scheduled on a thread's run loop. The worker threads which service dispatch queues can be terminated at any time and, in any case, don't run their run loop.