Itty Bitty Labs

Code and technical stuff from Itty Bitty Apps.

Queues to the Rescue

Often when developing applications you want to run some code but only if your application is in a particular state and if the app is not yet in yet in that state, do it later.

One approach would be something like the this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@interface MyClass : NSObject
- (void)doSomething;
@end

@implementation MyClass

- (void)doSomething
{
  if ([self isActive])
  {
      // do something
  }
  else
  {
      self.doSomethingWhenActive = YES;
  }    
}

- (void)didBecomeActive
{
  self.active = YES;
  if (self.doSomethingWhenActive)
  {
      self.doSomethingWhenActive = NO;
      [self doSomething];     
  }
}

- (void)didBecomeInActive
{
  self.active = NO;
}

@end

Unfortunately there are some problems with this simple (yet naive) solution.

The most problematic aspect of the simple solution is that we’ve introduced state into our class with the doSomethingWhenActive property. We have to remember when this flag should be turned on or off. We’re also potentially managing the state we’re basing our decisions on (the active/inactive state above).

Every time you introduce state, and behaviour that is dependent on that state, into a class you increase the potential for bugs to creep into the code. Eg, which code paths set and clear that state. Is it possible for the boolean state variable to get stuck on or off because we didn’t reset it in a particular code path, etc.

Another problem with this approach is that if we need to add some more behaviour that is performed when our class is “active” or returns to being active (or some other situation) we may have to add yet another boolean property and yet more conditional logic complicating our class’s behaviour and compounding the likelihood of bugs creeping in.

A more robust solution to this sort of situation is to use a work queue.

Here is an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@interface MyClass : NSObject
- (void)doSomething;
@end

@implementation MyClass

- (id)init
{
  self = [super init];
  if (self)
  {
      _activeAppTaskQueue = [[NSOperationQueue alloc] init];
      _activeAppTaskQueue.maxConcurrentOperationCount = 1;

      // The active app task queue begins in a suspended state.
      _activeAppTaskQueue.suspended = YES;
  }        
}

- (void)dealloc
{
    _activeAppTaskQueue.suspended = YES;
    [_activeAppTaskQueue cancelAllOperations];
}

- (void)didBecomeActive
{
  _activeAppTaskQueue.suspended = NO;
}

- (void)didBecomeInActive
{
  _activeAppTaskQueue.suspended = YES;
}

- (void)doSomething
{
  [self performBlockWhenActive:^(MyClass *object) {
      // do something with object
  }];
}

- (void)performBlockWhenActive:(void (^)(MyClass *))block
{
  if (block)
  {
      __weak MyClass *myObject = self;
      [_activeAppTaskQueue addOperationWithBlock:^{
          // Active app tasks might need to run on the main queue.
          // This depends on if your class manipulates UI.
          dispatch_async(dispatch_get_main_queue(), ^{
              block(myObject);
          });
      }];
  }
}

@end

In the code above the performBlockWhenActive: method is private and invocations of doSomething use it to enqueue work for execution later. You could make this more general by exposing the performBlockWhenActive: method publicly. Then clients of that class can choose whether they want doSomething to happen immediately or only when the app is active. Eg:

1
2
3
4
5
6
7
8
9
10
@implementation MyOtherClass

- (void)confribulateTheConfabulator
{
  [_stuff performBlockWhenActive:^(MyClass *object) {
      [object doSomething];
  }];
}

@end

Note: The code above uses an NSOperationQueue instead of a GCD dispatch queue so that tasks that have not been executed can be canceled when the MyClass instance is released. If you want all submitted tasks to execute no matter what, you could use a GCD dispatch queue. Then rather than cancelling all the operations before release you would ensure that the queue was resumed (and thus pending task blocks completed). Be careful with suspending/resuming GCD queues however. They will assert/crash your app if calls to dispatch_resume/dispatch_suspend are unbalanced or if you attempt to release a queue while it is suspended.

Also be careful about introducing retain cycles by making references to self (either implicit or explicit) in the task blocks. This is why I pass the MyClass instance that actions should be performed on into the block as a parameter to help avoid this sort of reference cycle.

So although this solution isn’t without its own potential pitfalls and problems I feel it is a better solution than the first simple attempt.

The queue based solution has the benefits of being more general, requiring less state, and executing the tasks in the order they are submitted with performBlockWhenActive:.

I recently employed this pattern in my own code when I needed an application to display a window to the user but only once the app had become the active application.

The call to performBlockWhenActive: was made in a part of the app’s lifecycle that did not guarantee that the application had completed launching yet. The same code however could also be invoked at times when the app was launched and active. By pushing the work into a queue that was only executing when the app was in the active state I was able to solve this issue with the minimum amount of fuss and also provide a facility to the application that could be used in other similar situations.


About Oliver

Oliver Jones is the Technical Director at Itty Bitty Apps. He spends his days building the iOS Introspection tool Reveal and playing Pinball. You can follow him on twitter @orj.