Search code examples
iosobjective-cthread-safetyobjective-c-blocksnsoperationqueue

Moving variables between threads with NSOperationQueue


The Background

I've got a singleton object that performs network requests on a background thread (created through an NSOperationQueue). This works just fine. The network request happens without clogging up the user interface.

The Problem

The singleton needs to make a call to [UIApplication sharedApplication] during the backgrounded network request. This call retrieves information about the app's state. The returned value from UIKit determines how the app proceeds. Of course, UIKit isn't thread safe and can only be called from the main thread. Thus, the request for app state information needs to be done on the main thread.

The problem is that this variable, containing the app state information, was set while on the main thread but needs to be accessed on a different thread. What is the best way to handle this?

Attempted Solution (1)

This solution results in a compiler error. appStateIsInForeground cannot be modified inside of a block.

 // Create variable on separate background thread

 // Configure the server connection based on the current app state
BOOL appStateIsInForeground = YES;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
    if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive) appStateIsInForeground = YES;
    else appStateIsInForeground = NO;
}];

// Access appStateIsInForeground variable from the same separate background thread

Attempted Solution (2)

Adding the __block specifier silences the compiler error. However, I feel that this solution could result in a race condition or throw an exception because of the attempt to access the variable on multiple threads.

 // Create variable on separate background thread

 // Configure the server connection based on the current app state
__block BOOL appStateIsInForeground = YES;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
    if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive) appStateIsInForeground = YES;
    else appStateIsInForeground = NO;
}];

// Access appStateIsInForeground variable from the same separate background thread

TL;DR

How can I create a variable on a background thread (initialized with NSOperationQueue), assign it a value on the main thread, and then read its value on the original background thread without creating a race condition or causing a thread-safety exception?

EDIT: While it may be safe to read a value from UIKit on a non-main thread, this question may still have broader application.


Solution

  • I'm not 100 percent sure that it is a bad idea to just READ value from UIKit on a background thread, however if it is so indeed, maybe it would be reasonable to use good old dispatch_sync function?

    __block BOOL appStateIsInForeground = YES;
    
    dispatch_sync(dispatch_get_main_queue(), ^{
    
        if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive) appStateIsInForeground = YES;
        else appStateIsInForeground = NO;
    });