Working on the Main Queue

In general, working on the main queue hasn’t changed from the original design. Assuming you’re working with an NSManagedObjectContext that’s configured to run on the main queue, you’d access that NSManagedObjectContext the exact same way as before.

The big difference is when your code is on another queue and you need to do some data work on the main queue. Getting that work onto the main queue has changed, fortunately for the better. This improvement is in the form of two methods: -performBlock: and -performBlockAndWait:.

Introducing -performBlock:

The goal of -performBlock: is to guarantee that a block of code is being executed on the correct queue, which is the queue that the NSManagedObjectContext is associated with. Therefore, if you have a block of code that you need to execute on the main queue against the NSManagedObjectContext associated with the main queue, you can do the following:

 NSManagedObjectContext *moc = ...; //Reference to main queue context
 [moc performBlock:^{
  NSFetchRequest *request = ...;
  //... Define the request
  NSError *error = nil;
  NSArray *results = [moc executeRequest:request error:&error];
  if (!results) {
  NSLog(@"Failed to fetch: %@\n%@", [error localizedDescription],
  [error userInfo]);
  }
  // Do something with the results
 }];

In this example, you retrieve a reference to the existing NSManagedObjectContext that’s instantiated against the main queue. From there you call -performBlock: and inside that block is where you do all of the work that needs to be performed on the main queue against Core Data.

The call to -performBlock: takes the block of code and puts in the “todo” list for the queue associated with the NSManagedObjectContext that it’s called against. As soon as that queue gets to the block of code, it will be executed. Generally the execution happens right away, but if that queue is busy with another task (for example, it has another block of code to execute), then the block will be performed later.

Calling -performBlock: isn’t a “blocking” call, which means that the queue that calls -performBlock: won’t be halted or paused and the line of code after the -performBlock: call will be executed immediately—most likely before the block is executed.

What this also means is that the -performBlock: call is re-entrant. While you’re inside one call to -performBlock:, you can kick off another call. Your second call to -performBlock: is guaranteed to be executed after the first call. Therefore, you could do something clever like this:

 NSManagedObjectContext *moc = ...; //Reference to main queue context
 [moc performBlock:^{
  [moc performBlock:^{
  NSError *error = nil;
  if (![moc save:&error]) {
  NSLog(@"Failed to save: %@\n%@", [error localizedDescription],
  [error userInfo]);
  abort();
  }
  }];
  NSFetchRequest *request = ...;
  //... Define the request
  NSError *error = nil;
  NSArray *results = [moc executeRequest:request error:&error];
  if (!results) {
  NSLog(@"Failed to fetch: %@\n%@", [error localizedDescription],
  [error userInfo]);
  }
  // Do something with the results
 }];

And the -save: call would be executed after the data manipulation code. This effectively gives you a try/finally pattern.

Introducing -performBlockAndWait:

There are plenty of situations where you want to execute code on the main queue but you want your background queue (aka the non-main queue) to wait for that execution to be completed. That’s where the API -performBlockAndWait: is used. The parameters are exactly the same, but the behavior is a little bit different.

The main difference is that this API call will block the calling queue until the block is completed. This also means that -performBlockAndWait: is not re-entrant.