A quick tip on how to use of Xcode breakpoint actions as an alternative to debugging with print statements. The examples and screenshots in this post are from Xcode 3 but also apply to the upcoming Xcode 4 though some of the details differ.
Debugging with print statements
Anybody new to coding will soon learn the technique of inserting print statements into code as a way to debug it. Over time as you learn about conditional compiler directives this can develop into more sophisticated approaches with debug statements being enabled/disabled at compile time.
With Objective-C those print statements generally become NSLog statements to make it easier to print objects. I have also previously mentioned conditional compilation for debug or distribution builds to ensure that the log statements don’t make it to production builds.
Using NSLog, preferably wrapped inside conditional compiler macros, is great when you want to maintain some debug statements permanently in the code. Examples where I have done that in the past would be to log some major failure or error condition. However when I am trying to debug an error condition it can be a pain to add and remove NSLog statements. I find you end up sprinkling debug code across many different source files which then have to be cleaned up when you are done (and of course when you debug you are rarely done when you first think you are done). Luckily there is a better way…
The debugger in Xcode makes it easy to set breakpoints on code and then inspect objects using the debugger console when the breakpoint is reached. However you can speed things up a lot by adding actions to the breakpoints that are executed when the breakpoint is reached. This gives you the flexibility of NSLog statements but in a much more manageable way.
I am guessing you already know how to add a breakpoint by clicking in the margin to the left on the source code editor. The enabled breakpoint is then indicated by a blue symbol as shown below:
To add an action to the breakpoint you can either right-click the blue indicator and select Edit breakpoint or click on the breakpoint in the Groups & Files window. Either way should drop you into the breakpoint editor window:
Xcode is like many other IDE’s in that it packs a lot of functionality into the user interface which is great if you know what all the buttons and icons mean but I suspect in practise means a lot of the functionality is left undiscovered. The interface to create breakpoint actions is one example of this with some very cryptic and obscure icons. If you look at the previous screen shot and work from left to right the various columns are as follows:
- A disclosure icon to show/hide any actions for this breakpoint
- An icon indicating if this is a symbolic breakpoint or a file line breakpoint
- Name of the breakpoint
- Breakpoint enabled flag which when ticked indicates that the breakpoint is enabled.
- Name of the source file containing the breakpoint
- Any conditions used to control when the breakpoint will be triggered (e.g. only break when a variable has a certain value)
- A count of how many times the breakpoint should be ignored before triggering (useful if you want code to run a few times before breaking)
- Continue flag which when ticked indicates that the debugger should automatically resume execution once any breakpoint actions have been executed.
Adding a breakpoint action
If you click on the disclosure icon to the left of a breakpoint you open the interface to add an action to the breakpoint:
There a number of possible actions you can perform when the breakpoint is triggered. The two most useful (for me at least) are executing a debugger command and logging/speaking an expression.
Executing a debugger command
So assuming I have set a breakpoint on the following line of code:
self.timestamp = timenow;
To use the debugger to print the value of the instance variable timestamp each time the breakpoint is hit I could use the print-object (po) debugger command:
po [self timestamp]
Of course to be a workable alternative to inserting NSLog statements everywhere we also need to tell the debugger not to stop when it hits this breakpoint. We want the debugger to automatically resume execution once it has performed the debugger actions. To do that you need to tick the small box in the top-right hand corner above the action window. See the screenshot below for what this should look like:
Now each time the breakpoint is hit we will see the value of the timestamp instance variable logged to the debugger console without execution stopping.
Logging or speaking actions
A variation on executing a debugger command is to use the log breakpoint action. This can be used to directly log a message to the console or even speak the message. This can sometimes be useful when you are trying to understand the execution path that an application is taking. For example, if you are trying to understand when a view controller uses the viewWillAppear method you could add the following breakpoint action on the method:
Now each time the viewWillAppear method is executed for this view controller Xcode will say “viewWillAppear” and continue executing. This can be very useful when you are trying to understand the sequence that delegate methods are invoked.
Cleaning up when your done
The Groups & Files section of Xcode shows you a list of all breakpoints in your project which makes it easy to add/delete/enable/disable breakpoints across multiple source code files. In the following example I have 8 breakpoints defined, but only one of them is currently enabled (not easy to see in the screenshot but it is the first):
Deleting the breakpoints is easy from this view when you want to clean up but also being able to quickly disable and then later re-enable makes this much more manageable compared to NSLog statements. (One issue here though is that it is not clear from the breakpoint name which source file the breakpoint applies to which can be confusing sometimes).
Xcode also allows you to export a set of breakpoints if you want to share them with other developers working on the same project (I have never had cause to do that but I guess it could be useful).
I should finish up this post by saying that I find the breakpoint editor in Xcode 3 is sometimes a challenge to work with. The UI is not always obvious and has a few quirks. Having said that it is a technique well worth having in your debug toolkit especially as Xcode 4 promises to clean up the UI a little.