Intermediate Debugging with Xcode 4.5

This is a tutorial for intermediate iOS developers, where you’ll get hands-on experience with some extremely useful debugging techniques. By Brian Moakley.

Leave a rating/review
Save for later
Share

Learn some intermediate Xcode debugging techniques!

Learn some intermediate Xcode debugging techniques!

The one single constant in software development is bugs. Let’s face it, we don’t always get it right the first time. From fat fingers to incorrect assumptions, software development is like baking cakes in a roach motel – except we supply the critters!

Luckily, Xcode gives us a myriad of tools to keep the nasties at bay. There’s obviously the debugger we know and love, but there’s a lot more it can do for you than just examine variables and step over code!

This is a tutorial for intermediate iOS developers, where you’ll get hands-on experience with some of the lesser known but extremely useful debugging techniques, such as:

  • Getting rid of NSLog in favor of breakpoint logging
  • Getting rid of comment TODOs in favor of compiler warnings
  • Breaking on conditions with expressions
  • Dynamically modifying data with LLDB
  • And much more!

You see, for me the goal is to be a lazy developer. I’d rather do the heavy work up front so I can relax on the backend. Thankfully, LLDB (Xcode’s integrated debugger) values my martini time. It provides great tools so I don’t have to be glued to my computer all day and night.

Let’s take a look at these tools. Pull up a bean bag chair. Crack open your favorite beverage. It is time to get lazy! :]

Note that this tutorial assumes you already know the basics about using the Xcode debugger. If you are completely new to debugging with Xcode, check out this beginner debugging tutorial first.

Getting Starting

I put together a sample app for this project. You can download it here.

The app is called Gift Lister. It tracks gifts you might want to buy for people. It’s like Gifts 2 HD which recently was awarded Most Visually Impressive by this site. Gift Lister is like Gifts 2 HD but far far worse.

For one thing, it’s filled with bugs. The developer (myself in a different shirt) was being ambitious and tried to fix the app the old fashioned way. Yes, it’s still broken :]

This tutorial will walk you through the steps on how to fix the app while being as lazy as possible.

Okay, let’s get started, but don’t feel the need to rush :]

Open up the project and take a look around the various files. You’ll notice that the app is a simple front end to a basic Core Data backing store.

Note: If you do not know Core Data, don’t worry! Core Data is an object persistence framework which is a whole tutoria to itself. In this tutorial, you will not diving into the framework nor will you be interacting with Core Data objects in any meaningful way, so you don’t need to know much about it. Just keep in mind that Core Data loads objects and saves them so that you don’t have to.

Now that you’ve taken a look around, build and run the app.

Not surprisingly, the app crashed. Let’s fix it up.

Setting up the Debugger Console

The first thing to do whenever you start a debugging session is to open the debugging console. You can open it by clicking this button on the main toolbar:

While the button is nice and convenient, clicking it each and every debug session provides unnecessary wear and tear on your fingertip. :] This is why I prefer to let Xcode do it for me.

To do so, open Xcode preferences by pressing ⌘, or by going to the application menu and selecting Xcode\Preferences. Click the Behaviors button (the button with the gear over it).

Behaviors Dialog

Click the ‘Starts‘ item on the left hand side of the dialog. You will see a bunch of options appear on the right hand side. On the right hand side, click the seventh checkbox and then select ‘Variables & Console‘ on the last dropdown.

Do this for both ‘Pauses‘ and ‘Generates Output‘ items which are located just underneath the ‘Starts’ item.

The ‘Variables & Console’ option tells the debugger to show the list of local variables as well as the console output each time a debugger session starts. If you wanted to view just the console output, you would select ‘Console View’. Likewise, if you wanted to see just the variables, you would select the ‘Variable View’.

The ‘Current Views’ option defaults to the last debugger view on your last debugger session. For example, if you closed Variables and opted to just the view the console, then just the console would open next time the debugger was started.

Close out the dialog, then build and run.

The debugger will now open each time you build and run your app – without you having to go through the major bother of clicking that button ;] Although it only takes a second to do that, it adds up. And after all you’re trying to be lazy! :]

The NSLog Jam

Before continuing, it is important to review the definition of a breakpoint.

A breakpoint is a point of time in a program that allows you to perform actions on the running program. Sometimes, the program may pause at the designated point to allow you to inspect the program’s state and/or step through the code.

You can also run code, change variables, and even have the computer quote Shakespeare. You will be doing all these things later in the tutorial.

Note: This tutorial will be covering some of the advanced uses of breakpoints. If you are still wrapping your head around some of its basic uses such as stepping-in, stepping-out, and stepping-over, please read over the My App Crashed, Now What? tutorial.

Okay, now build and run the app. This is the result of your first attempt at running this app:

Can feel you the hours ticking away?

This project needs a little sanity. Currently, you cannot see the source of the compile error. To find it, you need to add an exception breakpoint to track down the source of the error.

So switch to the breakpoint navigator as shown below:

Then, click the plus sign at the bottom of the pane. From the menu, select Add Exception Breakpoint.

You should now see this dialog:

Exception Breakpoint

The Exception field gives you the option of activating the breakpoint in Objective-C, C++, or All. Keep the default option of All.

The Break field in the dropdown allows you to pause execution on whether an error is thrown or caught. Keep it selected on thrown. If you are actually making use of exception handling in your code, then select ‘On Catch’. For the purposes of this tutorial, leave it ‘On Throw’.

We will discuss the final two fields later in this tutorial. Click the Done button and then, build and run.

This time the result is much cleaner.

Take a look at the debugger console It is filled with log messages. A lot of them appear unnecessary.

Logging is critical to debugging code. Log messages need to be actively pruned else the console becomes littered with “noise”. Sifting through all that noise takes away from time on the golfing range, so it’s important that it is removed, otherwise you’ll be wasting more time on a problem than it deserves.

Open the AppDelegate.m and you should see a bunch of old messages in didFinishLaunchingWithOptions. Select them all and delete them.

Let’s find the next set of log statements. Open up the search navigator, and look for in NSLog(@”in viewDidLoad”);

Click the search results and FriendSelectionViewController.m will open to the line with log statement.

At this point, the effort you are putting into managing your log statements is starting to accumulate. It may not seem like a lot, but every minute does add up. By the end of a project cycle, those stray minutes can easily equate to hours.

The other disadvantage of hard coding your log statements is that each time you add one to the code base, you take a risk of injecting new bugs into your code. All it takes are a few keystrokes, a little autocomplete, then a small distraction – and your once working app now has a bug.

It’s time to move those log statements out of the code to where they belong. Breakpoints.

First, comment out both of the logging statements. Next, add a breakpoint by left clicking it in the gutter besides each of the statements.

Your code window should look like this:

Control click or right click the first breakpoint and select ‘Edit Breakpoint’. From the dialog, select ‘Log Message’ from the Action dropdown. In the first text field, type ‘in viewDidLoad’. The dialog should now look like the following:

Click the done button, then build and run. You should now see ‘in viewDidLoad’ in the console – but now it’s done with breakpoints instead of NSLog statements!

Note: Througout this tutorial, you will be clicking build and run after each breakpoint modification as its quicker to explain. The key point to remember: breakpoints are a runtime addition. You can add as many of them as you want during the execution of your program. This includes NSLog statements.

There is one major problem. The program is stopping at that breakpoint when you want it to continue. Changing that behavior is simple.

Control click or right click the breakpoint and select ‘Edit Breakpoint’. At the bottom of the dialog, click the ‘Automatically continue after evaluating’ checkbox. Now build and run again.

This time it correctly logs the message, only it pauses on the second breakpoint.

Control click or right click the second breakpoint. Select ‘Log Message’ in the action dropdown, then type “Loading friends…”. At the bottom of the dialog, click the ‘Automatically continue after evaluating’ checkbox. Click done. Now build and run again.

The app works great until it crashes. You can’t have everything :]

Believe it or not, you’re still doing too much work. Control click or right click the first breakpoint and replace “in viewDidLoad” with %B. Now run the app again. The console should look like this:

The %B prints out the name of the containing method. You can also use the %H to print out the number of times the method is being touched. Simple expressions can also be included.

So you could write: %B has been touch %H times. The console will read: -viewWillLoad has been touched 1 times.

Before you actually fix the crashing bug, let’s have one last bit of fun. Control click or right click the first breakpoint. Click ‘Edit Breakpoint” from the menu. In the dialog, click the plus button. This button allows you to add multiple actions to a single breakpoint.

Select the “Log Message” action only this time, type “To be, or not to be”. Press the “Speak Message” radio button, then click done. The dialog should look like this:

Now build and run and enjoy the performance.

Note: Novelty aside, this feature can actually be useful! Audio messages can be especially useful when debugging complicated networking code and the like.

Unfortunately, besides printing out simple string messages, the Log Messages action doesn’t have the same flexibility of NSLog. For that, you really need to add some Debugger Actions.

To demonstrate, you’ll fix the crashing bug. Build and run and then let the program crash. The stack trace reads:

<span style="color: #110000; font-weight: bold;">*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+entityForName: nil is not a legal NSManagedObjectContext parameter searching for entity name 'Friend'
<span style="color: #110000; font-weight: bold;">*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+entityForName: nil is not a legal NSManagedObjectContext parameter searching for entity name 'Friend'

Something is not working in Core Data.

Scanning the code, you see that the NSManagedObjectContext is being pulled from the DataStore object. You have a hunch that the DataStore is probably the cause of the problem. The DataStore is not part of Core Data. It is a custom singleton object used to encapsulate some of Core Data’s central objects.

Add a breakpoint underneath this line: DataStore *dataStore = [DataStore sharedDataStore];

Control click or right click the breakpoint, click ‘Edit Breakpoint’, then select select “Debugger Command” from the dropdown. In the text field type the follow:

po dataStore
po dataStore

Click the “Automatically continue after evaluating” checkbox and build and run.

As you suspected, the dataStore is nil.

Note: po is a debugger command that will print out the contents of an object. If you need to print out the contents of a primitive, use p instead. There are a lot of debugger commands that are beyond the scope of this tutorial. To learn more of them, head over to the LLDB documentation.

Open DataStore.m and you’ll see in the sharedInstance method that a nil value is being returned. Change the return value from

return nil
return nil

to

return sharedInstance
return sharedInstance

Build and run. Hooray, the app is (kind of) working!

Gift Lister - now running!

Contributors

Over 300 content creators. Join our team.