Logging with Xcode Breakpoints

If you don’t already know about it, setting actions for breakpoints in Xcode is a great way to monitor repetitive code without adding a bunch of NSLog() to your code.

After setting a breakpoint, right-click and select “Edit Breakpoint…”. Then select “Debugger Command” for the Action. You can enter any valid debugger command at this point. When you check “Automatically continue after evaluating”, the debugger will run your command and your code doesn’t stop. Sweet.

This approach works great when you want to log the contents of a variable by entering a command like po myVar. But there’s a problem: I often want to know where this variable is getting logged, so I ended up creating another breakpoint with a script print "myMethod in myClass" (in lldb) or echo myMethod in MyClass\n (in gdb) command.

Great. Now you have to manage two breakpoints instead of one. Worse, when you have code like this:

for (MyClass *class in aCraploadOfClasses) {
    if (class.propertyThatIsRarelySet) {
        [self doSomethingWith:class];
    }
}

You can’t use the two breakpoint trick when you want to track class before it’s sent to -doSomethingWith:.

It’s taken me a way too long to come up with what is now a totally obvious solution: make the debugger command call NSLog!

For example:

Let’s break down the debugger command a bit:

p (void)NSLog(@"%s: %@", _cmd, syncItemId)

The _cmd is a C string with the current method name (it’s passed to every Objective-C method by objc_msgSend). At runtime, _cmd makes a very good replacement for __PRETTY_FUNCTION___. The syncItemId is the thing I was tracking in several different methods of my class. The void return value is needed to keep the p command happy.

This little discovery is going to save me tons of time: no more dropping into Xcode to add a line of code that you’ll end up deleting after tracking down the problem. Hope it helps you, too!

A Retina Web

A tweet this morning by James Duncan Davidson got me thinking about the future of Retina images on the web. Before I start talking about the future, let’s take a quick look at the past.

There was a time, not too long ago, where many of us were trapped behind 56kbps modems with 256 color displays. Much time and effort was spent on hacks that made images work well within those constraints. High compression settings and dithering with limited color palettes all combined to get the best quality from the hardware of the day.

Thankfully, as hardware became more capable, these hacks have become a distant memory. Our “solutions” completely ignored the effect of increased bandwidth and didn’t age gracefully.

Now, let’s look at the present. We’re at another tipping point with network speeds and display resolutions. Mobile networks are getting a lot faster and displays have reached a point where we can’t see individual pixels. And attempts to work around current limitations feel dangerously close to those of the past.

There is a movement to improve HTML so it’s more responsive to a wide range of devices. There’s also Javascript that replaces images dynamically. To me, there are a lot of similarities between these techniques and the hacks we had in the late 90’s.

The main problem is that it’s very difficult to determine what type of image is best for the viewer. Screen size is no longer a good metric: my iPad 3 has more pixels than my desktop computer. Similarly, my iPhone’s LTE is nearly an order of magnitude faster than the wired DSL connection in my office. As the device and network ecosystems become more diverse, this problem will continue to get harder to solve. (Remember how much fun sniffing User Agent strings was?)

The only sensible way to move forward is to use a “one size fits all” approach.

Of course, your visitors with the most capable hardware and network won’t be served the most optimal image. Nor will those who have older screens on slower connections.

Your job is to figure out what aggregate size works best for your audience, not to try and outguess what kind of device or network I’m using. Use image compression to create a payload that keeps everyone happy and as needs change adjust that size accordingly.