Learn Objective-C on the Mac - Mark Dalrymple [152]
This simply defines a couple of outlets to the two objects visible in our GUI, and an action method to be triggered by the button. Now enter the following code for SlowWorkerAppDelegate.m:
As you can see, the “work” of this class (such as it is) is split up into a number of small chunks. This code is just meant to simulate some slow activities, and none of those methods really does anything time consuming at all, so, to make it interesting, each method contains a call to the sleep() function, which simply makes the program (specifically, the thread from which the function is called) effectively “pause” and do nothing at all for the given number of seconds. The doWork: method also contains code at the beginning and end to calculate the amount of time it took for all the work to be done.
Now, open up MainMenu.xib, and put an NSButton and an NSTextView into the empty window, laying things out as shown in Figure 16-1. Connect the app delegate’s two outlets to the relevant controls, and connect the button’s action back to the app delegate’s doWork: method. While you’re in there, configure the NSTextView a bit, deleting the example text from the view, and turning off the Editable checkbox in the Attributes Inspector.
Now save your work, and hit Build & Run in Xcode. Your app should start, and clicking the button will make it work for about ten seconds (the sum of all those sleep amounts) before showing you the results. About five or six seconds in, you’ll see that the mouse cursor changes to the spinning-disk cursor, and it stays that way until the work is complete. Also, during the entire time, the application’s menu is unresponsive, along with the window controls. In fact, the only way you can interact with your application at all, besides killing it with Mac OS X’s Force Quit window, is to move its window around, because the OS itself handles that. This is exactly the state of affairs we want to avoid! In this particular case it’s not too bad, because the application appears to be hung for just a few seconds, but if your app regularly “beachballs” this way for much longer than that, you’ll end up with some unhappy users—and maybe even some ex-users!
Threading Basics
Before we start implementing solutions, let’s go over some of the basics involved in concurrency. This is far from a complete description of threading in Mac OS X or threading in general. For that, you’ll need to look elsewhere. We just want to explain enough for you to understand what we’re doing in this chapter.
In most modern OSes (including of course Mac OS X), apart from the notion of a process, which contains a running instance of a program stored on disk, there’s also the notion of threads of execution. Each process can consist of multiple threads, which all run concurrently. If there’s just one processor core, the OS will switch between executing multiple threads, much like it switches between executing multiple processes. If there’s more than one core available, the threads will be distributed among them just like processes are.
All threads in a process share the same executable program code, and the same global data. Each thread can also have some data that is exclusive to the thread. Threads can make use of a special structure called a mutex (short for mutual exclusion) or a lock, which can ensure that a particular chunk of code can’t be run by multiple threads at once. This is useful for ensuring correct outcomes when multiple threads access the same data simultaneously, by locking out other threads when one thread is updating a value (in what’s called a “critical section” of your code). For example, let’s say your application implements a banking system, where an account balance can be modified as part of a transaction. In a multi-threaded system, you need to protect the section of code that adds or subtracts from the account balance, to eliminate the possibility that two threads are both messing with it at the exact same time. Otherwise, you might have both threads reading the old balance more or less