Learn Objective-C on the Mac - Mark Dalrymple [158]
If you’ve done much C programming, you may recognize that this is similar to the concept of a function pointer in C. However, there are a few critical differences. Perhaps the biggest difference, the one that’s the most striking when you first see it, is that blocks can be defined inline in your code. You can define a block right at the point where it’s going to be passed to another method or function. Another big difference is that a block can access variables available in the scope where it’s created. By default, the block makes a copy of any variables you access this way, leaving the originals intact, but you can make an outside variable “read/write” by prepending __block before its declaration.
As we mentioned a little while ago, blocks really shine when used with GCD. GCD includes a set of functions that accomplish the same sorts of things that NSOperation and NSOperationQueue do, but with a different spin. The main difference is that, instead of explicitly creating a bunch of operations, optionally declaring inter-operation dependencies, and then assigning adding the operations to queues, with GCD you call a function that takes a block and adds it to a queue in a single step; interoperation dependencies don’t need to be declared, because the code in the block executes sequentially.
Improving SlowWorker a Second Time
To see how this works, let’s take a look at the original form of SlowWorker’s doWork: method. To get to it, open up the copy of the original SlowWorker project directory you made earlier, and use that for the rest of the changes we’re going to show.
We can make that method run entirely in the background by wrapping all the code in a block, and passing it to a GCD function called dispatch_async. This function takes two parameters: a GCD dispatch queue (conceptually similar to an NSOperationQueue) and a block to assign to the queue. Take a look:
The first line grabs a pre-existing global queue that’s always available, using the dispatch_get_global_queue() function (Unlike NSOperationQueue, with GCD there’s always a global queue available, ready to dispatch work to the background threads). That function takes two arguments: the first lets you specify a priority, and the second is currently unused and should always be 0. If you specify a different priority in the first argument, such as DISPATCH_QUEUE_PRIORITY_HIGH or DISPATCH_QUEUE_PRIORITY_LOW
(passing a 0 is the same as passing DISPATCH_QUEUE_PRIORITY_DEFAULT), you will actually get a different global queue, which the system will prioritize differently. For now, we’ll stick with the default global queue.
The queue is then passed to the dispatch_async() function, along with the block of code that comes after. GCD then takes that entire block, and passes it off to a background thread, where it will be executed one step at a time, just like it when it was running in the main thread.
Don’t Forget That Main Thread
There’s one problem here: AppKit thread safety. Remember, messaging any GUI object including our resultsTextView from a background thread is a no-no. Fortunately, GCD provides us with a way to deal with this, too. Inside the block, we can call another