A bug report is filed. It has these ominous words: iOS 5 bug. Dun-dun-duuun. What do you do?
Twice now, this has happened to me at work. Here’s a technique I’ve used to find and fix the problems. It’s served me well!
First, fire up Xcode 4 and see if you can reproduce the iOS 5 bug in the iPhone 5.0 Simulator (or the iPad 5.0 Simulator, depending on your app). If you can get it to happen in the simulator, it makes life that much easier, because we’ll be switching back-and-forth between iOS versions.
Now rerun the scenario in the iOS 4.3 Simulator. Can you verify that the bug doesn’t occur there? Then we’re in business. You have a genuine iOS 5 bug!
Finding iOS 5 bugs: Where do we even start?
What could be causing the problem? Where do we begin to diagnose an iOS 5 bug?
First, don’t even bother reading through your code to look for anything suspicious. You won’t find anything. After all, you verified that the problem doesn’t occur on an older version of iOS. Under certain circumstances, you can say, “This code works.” There isn’t anything obviously wrong. So what’s going on?
Remember that we’re writing against an elaborate framework, relying on iOS to invoke our methods. With each new release of iOS, Apple isn’t just adding features; they’re also trying to improve battery life. If they find any call sequences they can eliminate, they’ll do it. But sometimes, this means an inefficiency you used to rely on may have been removed, causing one of your methods to no longer be invoked, or invoked in a slightly different order.
Method logging to the rescue
So now your job is to find what’s changed in the way your methods are invoked. To do this, we’re going to add method logging. This allows us to determine the order of method calls.
Go to the view controller in question. At the beginning of each method, add this statement:
printf("%s\n", __PRETTY_FUNCTION__);
Don’t do this manually if you can help it! If you use the following style of braces and indentation, you’re in luck:
- (void)foo
{
// code here
}
Find and replace all occurrences of newline, open brace, newline, and whatever you use to indent your code (spaces or a tab). You want to end up with
- (void)foo
{
printf("%s\n", __PRETTY_FUNCTION__);
// code here
}
Compile to see if you’ve introduced the printf anywhere that doesn’t make sense (like a struct) and get rid of the those.
If you use K&R-style braces like
- (void)foo {
// code here
}
you’ll have to be a bit more clever, because we only want the opening braces of methods, not of any other block statements. If you use this brace style with tab indents, you can use regular expression find-and-replace: search for ‘ {\r\t(\w)’ and replace it with ‘ {\r\tprintf("%s\n", __PRETTY_FUNCTION__);\r\t\1’. Note the leading spaces. Here’s how it looks in BBEdit:
Spot the difference!
So what does that odd-looking statement do? And why printf, why not NSLog?
__PRETTY_FUNCTION__ is a special gcc identifier for the name of the current function. The function name is formatted to match Objective-C (or C++) syntax. Here’s the kind of output you should get:
-[MyViewController viewDidLoad]
-[MyViewController viewDidAppear:]
-[MyViewController viewWillAppear:]
What we want is to run once on iOS 4, logging the calls. Then we run again on iOS 5, and compare the results. By using printf instead of NSLog, we avoid the timestamps that NSLog outputs. We want to make the two logs as similar as possible, in order to bring out the differences.
Are you ready? Let’s do it!
- Turn off any other logging you use.
- Run your scenario on the iOS 4 Simulator. Copy the log to a new document, and clean it up so it only shows the method calls.
- Re-run your scenario on the iOS 5 Simulator. Copy the log to a new document, and clean it up so it only shows the method calls.
- Compare the two and find the differences. I like using BBEdit, which has a handy Search menu item “Compare Two Front Documents”.
Somewhere in the differences, there’s a method that’s called on iOS 4 but not on iOS 5. You’ll probably notice several differences, so you’ll need to do some sleuthing to identify which one causes the iOS 5 bug. Here are some things to try:
- Narrow the list of differences down to the likely candidates related to the functionality that’s missing.
- Try setting breakpoints on the candidate methods, and run your scenario on iOS 4.
- When you stop at breakpoints, note the call stack.
- Set breakpoints further back on the call stack, and step through on iOS 5.
Be patient: You have two lists of method calls and clear differences between them. It’s in there somewhere. Once you find what’s not being invoked on iOS 5, add a direct call to it.
If you can confirm that this changes fixes the iOS 5 bug, make sure it doesn’t introduce a different problem on iOS 4. (Don’t forget to clean out the printf statements!)
Good luck, and happy hunting. If this helps you track down an iOS 5 bug, let us know in the comments below!
Photo by Kaustav Bhattacharya (license)







That’s not really handy. I’d rather have a #define with DLog(@”foo”) with a custom NSLog statement already including the pretty function. Then you could just do the DLog(@”") and you’d get the same result.
I’m pretty sure you can achieve automatic method logging with DTrace and a custom probe which prints class/function on method entry (or even exit).
Also the find/replace regexp may find places after if, for, while, synchronize and basically anything that has a scoped block using braces.
Eimantas, you’re welcome to turn this into a macro, of course. That would be useful if you want to add tracking by hand. But for tracking down iOS 5-specific bugs, I want to do it across all methods in a single file, so (after doing it by hand the first time) I now use find/replace.
The problem with using NSLog for this is you don’t want timestamps, because they’ll make it hard to do the diff.
If you’re handy with DTrace, more power to you. But my approach requires no additional tools.
The find/replace works without messing up your other blocks, because it searches for open curly, carriage return, a single tab, followed by non-whitespace. Try it, I’ve used this at work! The only thing it messes up is struct declarations.
Hey Jon,
I have a lag problem that occurs only in ios 5 (see http://stackoverflow.com/questions/8704300/cabasicanimation-lag-issues-with-ios-5-and-remoteio for details).
Since my code hasnt changed between ios versions my suspicion would be that something has changed in the underlying cocoa classes that support ios 5 … would it be worth trying your method here do you think or is it unlikely to find any problems as a result of changes in the OS architecture?
Thanks!
Dave, one of the problems I solved with this technique was around scrolling performance; the display would stutter and be non-responsive for a time. Multiple people had looked at this bug for some time without success (including me). So it may help! My guess is that the problem isn’t in the animation itself, but something else that’s interfering with the animation.