The Problem with Debugging in Xcode
This is one of those things in Xcode that could be really neat, and could be completely useless. Any long time user of Xcode should know by now that the variable debuggers/watch windows are pretty much the most useless things ever. Let’s say I have about the simplest example I can come up with. A simple class to represent a person.
1 2 3 4 5 6 |
@interface Person : NSObject @property (nonatomic, copy) NSString* firstName; @property (nonatomic, copy) NSString* lastName; @end |
1 2 3 4 5 6 |
@implementation Person @synthesize firstName=_firstName; @synthesize lastName=_lastName; @end |
There is no denying that this is just about the simplest thing we can probably do for a model object. So now let’s try and debug this object
1 2 3 4 5 |
-(void)doSomeWork { Person* person = [[Person alloc] init]; //do something with the person } |
We’ll just create a person and stop the debugger on the line after. Here’s what I get for free:
How wonderful! we know that the instance of person is an object! Glee! ALL HAIL APPLE! CREATOR OF THE BEST DEBUGGER EVER!
Did anyone note the sarcasm there? Admittedly, as damn useful as this isn’t, I am a huge fan of the console.
I mean, sure having to do po variable
for everything can be a little tedious, and I hated it at first, but once I got used to it, I get really annoyed using IntelliJ or Visual Studio. They have great variable views, but you have to traverse a potentially huge tree just to find out the value of 1 thing. Visual Studio has the immediate window, that I sometimes tinker around with, but usually you end up having to write complex statements for your watch variables, or just start outputting everything to the log. So while I think the variable view in Xcode is the second most useless feature Apple has ever given us1 the console makes up for it in some ways.
Putting aside that tangent for a moment, a while back with Xcode 5, Apple introduced to us Quick Look in debug mode. This was a pretty neat idea to be able to show a more graphical representation of your variables. If you have something like a UIImage, you can just look at what the image is in a previewer. But this still has the same problems as the variable view. Say I set a property on my object, and then I want to use the magical Quick Look to see if the property got set.
1 2 3 4 5 6 |
- (void)viewDidLoad { [super viewDidLoad]; self.someString = @"Hello World"; } |
Once again, I’m out of luck. when I stop in the debugger, even though someString is a variable compatible with Quick Look, I can’t just mouse over self.someString to inspect the variable OR to look at it with QuickLook. Instead I have to po self.someString which doesn’t use Quick Look so it’s ok for a string, but not for an image or a view. Alternatively I could do something like this
1 2 3 4 5 6 7 |
- (void)viewDidLoad { [super viewDidLoad]; self.someString = @"Hello World"; NSString* foo = self.someString; } |
so that I could Quick Look foo but I don’t want to have to do that everywhere, and it’s only a solution for 1 variable, what if I change my mind and want to Quick Look on another variable? My source is going to end up looking like a 1st year CS student was trying to debug. I’m a Professional, and I deserve something better!
Custom Quick Look
Xcode 6 introduces something that is almost great – custom Quick Look for your classes. The reason it is almost great is because you still have to manually manage what goes into your Quick Look, but that is needed because of the potentially destructive nature of debugging. There are some methods that when called will change the state of the application, and you don’t want the debugger to be calling those without your permission.
Custom Quick Look is pretty easy to implement. All you have to do is implement
1 2 3 |
-(id)debugQuickLookObject { } |
The return value just needs to be one of Xcode’s default supported objects.
For our Person object, we could do something as simple as this:
1 2 3 4 5 6 7 8 9 10 11 |
@implementation Person @synthesize firstName=_firstName; @synthesize lastName=_lastName; -(id)debugQuickLookObject { return [NSString stringWithFormat:@"%@\n%@", self.firstName, self.lastName]; } @end |
And now, we can Quick Look an instance of a Person Object – Note: we do still have to have an actual variable for our object, so self.person is not Quick Look’able, but if we have an instance it is. So this is still only sort of useful.
Other Debug Tricks
Any long time use of Xcode is probably already used to using po all the time, but there’s still some neat tricks. When you’re po’ing an object, you can override -(NSString*)description, but sometimes you don’t want to. There’s another method you can play with
1 2 3 |
-(NSString*)debugDescription { } |
or, in Swift2
1 2 3 |
func debugDescription() -> String { } |
When you implement this, you can leave the description method alone, and make it so your po variable will output something useful.
You can use this method to just start printing out everything in your object (by doing something like this)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#import <objc/runtime.h> -(NSString*)debugDescription { NSMutableString* output = [NSMutableString string]; [output appendFormat:@"----------------------------------------------- Properties for object: %@\n", self]; @autoreleasepool { unsigned int numberOfProperties = 0; objc_property_t *propertyArray = class_copyPropertyList([self class], &numberOfProperties); for (NSUInteger i = 0; i < numberOfProperties; i++) { objc_property_t property = propertyArray[i]; NSString *name = [[NSString alloc] initWithUTF8String:property_getName(property)]; [output appendFormat:@"%@: %@\n", name, [self valueForKey:name] ]; } free(propertyArray); } [output appendString:@"-----------------------------------------------"]; return output; } |
You could probably even do something with swizzling or some other Objective-C black magic to make this be the default implementation of debug description. Or you could create a category on NSObject and call this something else. The possibilities are endless.
UIView implements description but, what you may not know is that it also implements recursiveDescription. Documentation here. Though Xcode 6 has the super awesome view debugging which is more powerful.
Sometimes you’ll run into a situation where you know what method you end up at, but not how you got there, and you can’t attach the debugger (it happens for the really odd/hard problems). You can just output the stack trace like this:
1 |
NSLog(@"%@",[NSThread callStackSymbols]); |
There’s also some pretty damn amazing things you can do with breakpoints, like symbolic breakpoints, or Watchpoints. Give this article by Bryan Hardy on The Nerd Ranch for more info.
The Hierarchical view is takes the gold on most useless feature in Xcode. Seriously, I have never seen anybody use it in my life. ↩
make sure you either have NSObject as a super class, or implement the DebugPrintable protocol ↩