CocoaConf Raleigh Recap: Using Instruments to optimize your iPhone and iPad apps
Have you ever wondered why your app runs slow? Or have you heard about Xcode’s Instruments tool but didn’t know how to use it? In this tutorial, I will show you how to analyze your iPhone and iPad applications to figure out where the bottlenecks are, how to fix them, and the steps necessary to deliver a high performing app that functions well on all models of iPhones and iPads.
First we'll discuss how to profile your app using Xcode’s Instruments tool, and then I'll cover how to solve the issue we identify. Instead of just building your app like usual, you need to go to the Product Tab on top and click Profile, or "Command – I". This will build your app and automatically load it into Instruments.
Here is what you should see:
Today we are going to select the "Blank" Option and load it up to do a Time Profiler test. You can also just select Time Profiler from the screen above, but I want to show you how to do select a test so you can drag others in if need be.
Once it loads, we will click the "Library" Icon to load up the list of pre-made tests that Apple provides us to use in analyzing our app.
Scroll down the Library view and pick Time Profiler, and drag it to the Instruments column on the left
When you are ready, click the record button and watch as your app starts to run on the device or simulator.
For this example, I've made a hilariously bad iPhone app that purposely has sub-par code, to highlight how this tool works (you can download it at the link on the bottom of this blog post).
Load up that project, and perform the steps above. When Instruments runs, go to the side bar and click "Hide System Libraries" and "Show Obj-C Only." This will help pinpoint exactly where in your app’s code is sluggish.
If you did it correctly, you should see a tree in the window on the right that looks like this:
Looks like this one function is gobbling up a huge fraction of my apps time (this is actually a common spot for a bottleneck). Double click the highlighted row I showed you above, and you will see:
It conveniently highlights the code and the % indicator shows you exactly which lines are causing the problems. Looks like the formatter is taking a ton of time to alloc and init every time on every cell. (Yes, I warned you this is bad code :) ).
So lets turn that into a global property that only gets alloc’d and init’d once with the values we want. NSNumberFormatters are not thread safe, but this is done on the main thread so it isn't an issue.
Remove this code:
and put this code in the ViewDidLoad method:
formatter = [[NSNumberFormatter alloc] init]; [formatter setMaximumIntegerDigits:2]; [formatter setMinimumIntegerDigits:2];
Then create a property in the .h file:
@property (nonatomic, strong) NSNumberFormatter *formatter;
and put this in the top of the .m file:
Now the fun part, re-profile your code (Command – I) and watch the magic happen. Check out the Call Tree Window:
As you can see, our cellForRow: method now only takes 3.0ms and viewDidLoad takes 1ms, for a total of 4ms. cellForRow: used to take 7ms as you can see in the comparison below:
Click the line I've highlighted for you above, to see this:
The number formatter is now only alloc'd and init'd only once, instead of once every cell. This dropped our time in this small example from 7ms to 4ms, optimizing our app by 57%. This may be a small example, but that kind of percentage gain is incredible, and I’m sure you can see how powerful this tool can be in a large project.
Good luck, go analyze your projects and optimize! If you have any questions, please feel free to reach out to me at: firstname.lastname@example.org
Download the un-optimized (hey I gotta make you work some) sample app project below: