iPhone • 54:16
To create an immersive iPhone experience, your apps need to respond instantly to your users, start up quickly, and use power efficiently. Learn the techniques that optimize CPU usage to minimize power drain, make efficient use of available memory, and give your table views the smooth scrolling users expect. Discover this and a wealth of other best practices for making your iPhone applications perform at their best.
Speakers: Dave Myszewski, Peter Handel
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
I'm David Myszewski and welcome to the talk on optimizing performance on the iPhone applications. So I'm David Myszewski. I've been working on iPhone performance for over 3 years and I manage the iPhone performance team. And we have a lot of topics to cover. A lot about performance today. So to talk about performance we're going to divide the talk into 5 different sections. First, we'll talk a little bit about drawing and scrolling. Second, we'll talk a lot about Application Launch, then memory usage. A little bit about files and data.
And then finally Peter Handel will help me out and talk a lot about power and battery life to see how to get the best out of your iPhone. So why should we care about performance? Well, if you look at any sort of the tech specs you know that iPhone is not quite a Mac. It's, you know, certainly, the RAM is a lot more limited, and we've added to that with the iPhone 3GS. We have now doubled the RAM. But we still have a limited memory footprint and 40 million devices out there with 128 Megs of RAM.
So utilizing memory is very, very important on iPhone. And of course, you have different characteristics of the mass storage using Flash instead of a spinning media. And networking is very, very different. And of course, the processor is very, very great for a Smart Phone, but it's not what you find in the Mac.
So it's very important to keep in mind as you're designing your apps and as you're testing your apps on how to get the most out of your apps. And to start we'll talk a little bit about drawing and scrolling. So the first demo that was ever done on iPhone was scrolling the list. Lists are all over the place. Every iPhone app has them. And scrolling is very, very important to the iPhone experience, and so we'll show you a little bit about how to get the best out of your app so that you have great iPhone scrolling.
So we'll switch over here to the demo. And say you've written an app and you've only tested it in the simulator. You haven't tested it in the device yet. And you install it on your new iPhone and this is the iPhone 3GS. And you run your little Movies app, so this app just displays the latest movies. And you scroll the list and well, it's not quite great on the iPhone 3GS even though it's really, really fast. And then you.
Oh, sorry. This is the original iPhone. On the iPhone 3GS it's a little better, but still you can see it's a bit chunky. So we're going to show you how to make this list a lot faster on both of these platforms so that 40 million iPhone users out there will have a great scrolling experience. And then the ones who buy the new iPhone will have an even better experience.
We go back to the slides. So we have a lot of drawing technologies on Mac OS. Or on OS X on iPhone. We have the great Core Animation and Quartz 2D drawing systems. We have OpenGL ES for games. And of course we have all the UIKit APIs that give you a very high level approach and a lot of built-in classes that help you draw images and labels and switches and a lot of things very, very efficiently.
Now we have a ton of APIs in the UIKit that help you draw things as fast as possible. UI Image View, for example, allows you to take an image, pass it to the view, and we have a very fast pass through Core Animation and all the way down through the hardware to render the images quickly as possible and get it on screen so that your users will see their pictures immediately.
And of course we have other things like UI Label and a number of other classes in the software that if you use you'll get very, very efficient drawing and a lot of other capabilities that may be difficult to build. And for any views, you know everything is based on views on the UIKit. And views are great to animate around. You can fade them out. You can move them across the screen.
And so definitely take advantage of all the animation facilities that views provide. If you have a view, it's very easy to move it around and do all sorts of animations to make your app look better. Now there's a lot of techniques to making scrolling performance fast, and we'll go through each of these. Scrolling performance.
The first thing you should ever check is whether or not you have translucent views. I'll show you a tool that allows us to do that in a moment. But translucent views are very, very costly to render because if you have 2 views that overlap you have to look at both of the pixels to figure out well, what should I draw on the screen? They're difficult to composite.
So to make this a lot more efficient, if you have views that don't need to be translucent, then you can mark them as opaque. And that's a lot more efficient for the graphics system to be able to get that view that you gave it and get it on screen as quickly as possible.
So if you ever have a scrolling list, try to make all of your views opaque. The second tip that we give for any sort of drawing is definitely draw as little as you need to. A check for rectangle intersection is always going to be faster than drawing extra bits on the screen. So when it applies, definitely call setNeedsDisplayInRect and pass it only to the dirty region. And then in drawRect you can check that dirty region to make sure that you'd only draw what you need to draw.
It works the same way as it does in AppKit. And occasionally it's really, really useful to have objects that stick around a lot. And you may use them for every cell. If you have such an image you may want to cache them, and sometimes you may even want to cache the bit. So for example, in the Movies app we have ratings. We have G, PG, etc. And all of those images are going to be the same, and there's only 5 of them.
So we keep around those images so that when the time comes to draw them they're already ready and they're decompressed. And we can get them on screen as quickly as possible. So particularly in scrolling lists you may want to cache certain parts of your views. And the last little drawing tip that we give, which is mainly for OpenGL Games, is occasionally we have timers where say, you draw every 30th of a second so that you get your nice smooth frame rate.
And occasionally people will forget to invalidate the timers after some action. Say the user locks the screen. They try to invalidate the timer, but they don't actually invalidate it. So definitely if you have a situation where say, the user locks a screen, and then when they unlock it things are very, very slow.
You may have 2 timers around and you're trying to do twice the work in the same amount of time. So if you encounter a bug like that, certainly look at the number of timers that you have in the system and view sampler. And the last little hint that we give is for any sort of UI artwork.
Use PNG files because we optimize them at build time to try to get them in a format that the iPhone can read really quickly. And we do some compression on the PNGs so that they're as small as possible, and so that we can read them as quickly as possible.
So for any sort of UI artwork we always recommend PNGs. So how did we improve that app that we just made? Well, we need to take all of this advice that I gave before. You need to use opaque views when possible. That's the first thing. The second step is always avoid allocating views while you are scrolling.
Because allocating them, then say drawing the background, is very, very expensive. So if you're constantly creating table cells and views as you scroll, one easy way to get better scrolling performance is to make those views stick around. So if you look at this cell, what's a cell made up of? It's made up of a lot of different parts. And the example that I showed you was actually made up of about 7 different views.
So we had the UI image view for the movie poster. We have a label for the title. Another label because it was a different font style for the cast. And another label for all the text. So we have a bunch of views around, and of course a label or an image view for the rating. So we have a lot of views. And in addition to that you have the content view for the TableViewCell, as you learned earlier in the week. And you have the cell itself.
So when you count them all up that's actually 7 views. That's a lot of views to create and a lot to draw as you are scrolling through a list. So we provide an optimization that allows you to reuse table cells. So in the previous example we just had 7 views.
And if you view. And for example, the rating may not change. The title and the cast is likely to. But you'll get really good performance if you keep those views around and then just replace their contents. So you may change the name of the title, but the view itself is still around, so you have a lot less drawing to do. And to do that you give a cell an identifier. And then we reuse them using the TableView cellForRowAtIndexPath. And there's an API to DQ a cell. And once you DQ the cell, you update it with whatever new content that you have.
And if you didn't need to update the rating title, well, then that's work that you don't have to do. So we try to reuse them. So to show you a little bit about how this works conceptually, so before we use table cells we have some table cells that are coming on screen.
And when they go off the screen they go away. We don't want that. We want them to stick around after they go off the screen so that when the next cell comes up we reuse that. So when we change our code to reuse table cells it looks a lot more like this.
The cells come through, and then when they go offscreen we recycle them and use them again. So if the contents didn't change we don't have to do any work. And we don't have to do any work to reallocate the table cell, draw the background color, which is always going to be white and never going to change.
We avoid a lot of work by reusing table cells. One final optimization that you can make, and this particularly applies when you don't support editing or deletion and some of the other complicated behavior of table cells. If you have one table cell in which you are only going to view contents, like say, the iPod Album List or the iPod Songs List, or this MovieView list, you can make an optimization to make the graphic software and hardware do even less work.
And that's what we call collapsing the cell view hierarchy. So I mentioned we had 7 different views. And in the cell's content view we have the 5 in red. We have the 3 labels and 2 image views. And all of those are well and good. But we can get even better performance if rather than having separate views and making the graphics software figure out what their relationship is to one another, and whether or not they include one another, we can actually take all of those views and make one large view, called a MovieView, and simply draw the bits to the rectangle inside the drawRect method rather than having a bunch of separate views. And this actually can improve scrolling performance quite a lot in the situations in which it applies. And I'll show you how to do that in just a moment.
So to think about this conceptually you have the UITableViewCell, which has a content view and has all these other views. And all we're going to do is collapse all of those views into the one view. So you may not know right away why scrolling is poor. It may not be obvious. So we have a whole lot of instruments that will help you with various performance optimizations. Memory management, graphics, checking for leaks, and a whole lot of other things.
We have a really great set of instruments that just you, for whichever situation applies, you use a particular instrument, and you can often find what's wrong fairly quickly. And we also have Shark, which is a time profiling tool, which has very, very high resolution. And has some sophisticated data mining technology that allows you to view where the time is going on the CPU and what threads are doing.
So we have a lot of performance tools that will help you out. But it's important to use the tool in the right place. So for example, the simulator, as I mentioned, maps it really, really fast. The simulator will execute code way faster than the iPhone will, and possibly with a little bit different performance characteristics. So for the simulator you can check for things like memory usage, leaks.
Because memory management isn't going to be all that different on the simulator than it is on the device, whereas performance will be. So on the device you have your whole suite of tools available to you, and that's where you should do a lot of your performance analysis for anything that requires a lot of CPU time. So your scrolling optimizations and other things.
So use the simulator for rapid development and memory analysis, and use the device for basically any of that and everything you want it to. So how do we improve scrolling performance using these tools? And how do we translate what the tools are telling us into code? Well, let's take a look at our app. So first of all, I've taken a Shark trace of this.
So to take a Shark trace under the sampling menu there's a Network/iPhone profiling menu item. And your iPhone appears if it was connected to this computer. And then you click a button and you'll end up with a trace that looks something like this. There are a couple of different types of views.
One is the bottom up and. Sorry. One is heavy and the other is the top down view. I like the top down view for looking at scrolling performance because I can fairly quickly find my TableView code. So you'll notice this is the code, the CreateToPrepareCellForGlobal Row. This is the UIKit TableView code that happens right before it asks us for a cell. And if you'll notice during scrolling a lot of the time is spent just initializing cells, so that was what I mentioned before. That the cell has many different pieces to it. It has UI labels, which are taking up quite a lot of time. Seven percent over here.
And of course we have the initialization of the cell itself and all the other views that go along with it. So just initializing each cell takes about 20% of the time, so we'd really like to cut that down. And the way that we cut that down is through cell reuse, so let's take a look at that.
Let's take a look at the code. So in our code we have a controller that creates a TableViewCell, so let's look for the controller method. And when we go down to the cellForRowAtIndexPath you'll notice that when we create the TableViewCell here we have a reuse identifier API here, but we're not actually using it. So what we really want to do here is we want to create the reuse identifier, and we'll just call it. We'll call it like Movies Reuse Identifier.
So rather than creating one with a nil identifier we actually want a user reuse identifier. But we don't want to create the cell all the time. So the first thing that we want to do really is we want to see if there's a cell available to us. So to do that we use TableView DQ reusable cell with identifier. And then we just pass the identifier to that method.
Then we ask, did the cell exist? And if it doesn't then what we want to do is we want to create a new cell. And for this particular cell that's all there is for doing cell reuse. So that's cell reuse. And now the second thing that we want to look at in our scrolling performance is opacity.
So we have a Core Animation tool. If you choose RunWithPerformance tool there's a Core Animation tool here. Core Animation instrument. That allows us to see if anything is opaque in this TableView. So if we run with that you'll notice that the first debug option here is color-blended layers.
And this is a really useful option, because if I click that and then go to the iPhone, you'll notice that everything that is opaque is in green. And everything that's translucent is in red. Red of course being very bad. So you'll notice here that we have a number of things in red. In particular we have all of our labels are red.
And labels are actually really, really easy to keep opaque. So to keep those labels opaque we have an optimization in TableView and we'll take a look at this right now. So you may think that if you have some sort of label in your TableViewCell, that when the user taps it, it needs to turn blue. And if the cell is going to highlight it in blue, well, that means that the label sort of needs to be translucent.
But UIKit actually has an optimization that allows you to create labels and assign them background colors that aren't clear. And the TableView actually detects when the user clicks on it. And when the user clicks on it we swap out the background color. So you don't have to have a clear color for your UI labels.
[ Silence ]
So we have a function that creates all of our labels, and based on whether or not it's a big text or not it uses different fonts and metrics. And you'll notice that the background color is currently a clear color.
So we can actually make this a white color and that will allow us to make everything there, all of the labels opaque. And so if we build that and install it we can fire up the Core Animation instrument again, and actually it's still running so I'll just switch over.
And you'll notice that all the labels are now opaque. And if I turn the color blend layers off you'll notice that when I select the table cell that UIKit just does the right thing and shows the highlight without the background color of the label occluding the highlight. So everything looks right. And now we're getting closer. You can see it's a lot better than what it was before.
But there's still one more optimization that we can do, and that's collapsing the cell view hierarchy. So to collapse the cell view hierarchy you first look at your object model. So we have a Movie Summary cell, and that's a subclass of UITableViewCell, that has several different views. So these are the 5 views that I talked about before. We have labels for.
Oops. Sorry about that. So we have 5 labels, so notice this is a subclass of UITableViewCell. And we have our 5 different views. So if you'll notice under the data models folder, we actually have a data model for the Movie. And notice here that we have Title, Cast, Synopsis, Rating and a couple of poster images.
So we have all the data structures that we need from this data model here. And so that means we can get rid of all of these because rather than. So what we're going to do is create a view that's just a subclass of UI View. And then draw everything within that single view. So that means.
And because we have a movie in this particular data structure, we can get rid of all of the views because we're just going to draw them ourselves. So those will all go away. Now of course, this isn't the UITableViewCell anymore, so we'll call it a UI View. And it's not really a cell anymore, so let's call it Movie Summary View. And now if we go. That's all we need to change in the header.
And so if we go to the implementation now, you'll find a few things that we'll need to change. Of course we'll change the title. There's an initializer.
[ Silence ]
And because this isn't a TableViewCell anymore, we can actually get rid of all this View Creation and Adding Things to the Content View. And this is just a UI View subclass, so we can just make this on the KnitWithFrame method.
[ Silence ]
So that simplifies the initializer. And of course because we're not creating all of these things anymore, we can get rid of everything but the Movie Release in the alloc. So that's our initializers. Now we have a few more things that we can change. We're not using UI labels anymore, so we can get rid of all this block of code that creates it. But we'll remember these text colors and font sizes for later because we'll need to draw them in the same fonts.
So let me move this back over. So we need to do a few things. In the View Controller to create this cell. Before we were creating a Movie Summary cell and DQing an item. And now we actually need to change this so that we create a standard TableViewCell, and you'll see this in a second.
So rather than using our special UITableView subclasss, we create a standard cell. DQ it like normal and create a new one if we need it. And in the code path where we have to create a new one, we actually create a Movie Summary view. And then we give it a little tag so that it's easy to find later. And then we set a couple of autoresizing apps so that it behaves correctly. And then we add it to the content view.
So that's our one view, and everything else is the same. So now we have a TableView cell that now has our main view. And now the only step that's left is making our main view draw correctly. So to do that we need to change just a couple of things.
You'll notice in the cell we have a set movie method. And in the old way, when it was subclass of UITableViewCell, it actually had to set the content of each of the views. So we set a bunch of text labels, set the right images, and set the right image for the rating. And because we're not dealing with views anymore we can make all of that go away. We're going to display our content in drawRect.
And of course, rather than trying to relay up the cell, which we would need to do in the other world, we actually want to draw here. So we'll call SetNeedsDisplay, and for now we'll just redisplay everything. And then there's one last step, and that's the all-important drawing step. So to draw everything, of course we don't lay out subviews anymore.
We use the drawRect method. And we don't lay out subviews, and now our content isn't really the content view bounds. We just can say it's our own bounds. Now you'll notice as I rewrite to this, as I change this layout subview's method to a drawRect method, there really. Not a whole lot is changing. The geometry is all the same because all of the images that are displayed, all the text that's displayed is still in the same location in the cell.
So we're actually not going to change a whole lot of geometry code here. So if I change the title first, rather than figuring out where I should place the label and placing it somewhere to lay it out, we're actually going to set the color to black so that we can draw everything ourselves.
We'll pick a font, which was the same font that we had before. And then we'll just simply draw it at the same point that we were drawing it before. So we have a utility method here to get all of the geometry for this particular thing. And we use the line break mode tail truncation to truncate the title if it doesn't fit on the screen.
But the geometry was basically the same as it was before. Now the rating image is actually pretty easy too. Because we have a utility method that we've written to get us the image based on what the rating was. And of course we have some long geometry function to figure out where it should go. But the only thing we need to change is rather than calling set for an UIImage view, we actually drawInRect here, so that was pretty easy.
And in fact, the same thing holds for all of the other tech cells. That rather than setting the frame you'll simply use drawInRect, which is. Oh, sorry. We'll use the APIs here to draw text at a point, which is the UIKit string editions. And so you'll notice we draw here. We draw here. And of course for the poster image we don't change the geometry at all. We just draw it again. So what we've done is we've changed our geometry. Or kept our geometry the same but changed where we've drawn.
[ Silence ]
And that was actually Movie.posterimage. And naturally I have a couple of errors, and that's because I had this guy sitting around just in case I need it. And we called it MovieSummaryView.h, so get rid of that. And we have a couple errors. Oops. This is actually MovieSummaryView. Oops.
[ Silence ]
Oops. Sorry.
[ Silence ]
[ Laughter ]
[ Silence ]
And now if we switch back to our MovieView, you'll notice it actually scrolls really, really smoothly now. So we made 3 changes. We changed the opacity so that everything was opaque. We reused table cells. And then we changed the view hierarchy so that we only had 1 view. And with that you have pretty good scrolling performance.
[ Clapping ]
[ Silence ]
So to summarize, you draw as opaque. Draw as little as you can. Reuse table cells. Use things for your images. And all of this really gets back to making as effective use of the software as you can. And next we'll talk just briefly about Application Launch.
Application Launch is really, really important for iPhone users because we are constantly going back and forth between our Mail app, our SMS app, our Facebook app. One of the apps you all have written. We're going back and forth between apps constantly. And so it's really, really important to get your UI on screen as fast as possible so that the user will feel that the overall system is really, really fast. And there's one more little bit here.
Which is that if you take more than 20 seconds to launch, then the iPhone will terminate your app. So please make it as fast as possible and don't get your app terminated. So you need to design your apps for a very, very quick launch. So what does that mean? Well, if you have a giant dataset you want to load that dataset lazily. And you want to use a very small nib for the initial UI.
If you use that nib file and have all sorts of views that aren't really used right away, then you may want to break that up into a lot of different pieces. And one thing that happens quite frequently is a lot of times you load more images than we need to because it's easier to write the code that way.
But try to only load the images that you really, really need on launch. And of course, don't perform networking operations in applicationDidFinishLaunching. It may work some of the time. For example, reachability checks. It may return quickly, or it may not return for 10, 20 seconds. It's really dependent on the network conditions. So definitely don't perform synchronous networking operations in applicationDidFinishLaunching If you're using asynchronous APIs then it's OK to do that.
So just a couple of examples. In our software in the phone app and basically any app that we have that has the button bar at the bottom, we can only load the visible UI so you don't go off and load anything that's not going to be appearing on screen.
And you wouldn't load all of these images if you were on a different view. In the YouTube app we actually have a lot of networking to do on launch. So what do we do? We display as much of the UI as possible. We display the separator and the bottom button bar.
And then we display a little loading progress indicator so that the user knows that we're actively working and trying to fetch the data that's required for YouTube. So that's a very, very brief overview of Application Launch. And now we go into one more topic which is really, really important on iPhone.
And probably the one that people have the most difficulty with, and that's memory usage. So the iPhone OS is a modern OS. It has a virtual memory system. But the key difference between iPhone OS and the Desktop OS's that many are used to developing for, is that there's no swap file.
So you have access to all of the standard great C methods like mmap, munmaps that allow you to have large data pieces in file, but the OS can swap out as it needs memory. But there's no swap file. So once you've allocated enough memory and the system's running out of memory, we start giving you memory notifications. And eventually we may have to terminate apps if memory usage keeps growing. So it's really important to keep your memory footprint down on iPhone.
And we'll go through a few things you can do here. One is of course iPhone OS has the standard sort of APIs that you're used to on Mac OS X. We have Objective-C where we manage everything through your contain accounts. We don't have garbage collection, but we have all the standard C and C++ memory management functions. So we're going to go through just a few areas of memory usage. Static memory, dynamic memory, application footprints, and memory warnings. We'll start out with static memory, mainly because it's a performance optimization.
And so static memory is basically some of the. One example of that is the code that you've compiled. Where you want to the make that you've compiled as small as possible. So if you have giant files that you're not using, you don't want to compile those into your code.
And one thing that's great about ARM is that we have a couple of instruction sets that allow you to reduce your code size. So on the original iPhone and the iPhone 3G, we have the choice between RNB6 and Thumb. RNB6 and RNB7 for the iPhone 3GS are 32 bit instruction sets. Both of them allow access to the floating point registers, and both of them you can use whenever you want them.
But on the iPhone and iPhone 3G there are some cases where you want to use Thumb. And in fact we turn it on by default, as I turn the graphics apps. Thumb is a 16 bit instruction set, but doesn't provide access to things like floating point registers. So in order to.
So if you do any floating point operations we have to Thumb back to ARM and then perform some instructions in ARM and then come back to Thumb mode, and that has some overhead associated with it. So if you have really, really intensive floating point apps like games, you may not want to use Thumb. But for all other apps, apps like the one that I just showed you that simply move around views in places and show some basic UI, Thumb is perfectly great for that.
So you don't want to use it all the time, but a lot of times it's appropriate. Now the iPhone 3GS you have the choice between RNB6 and. Oh, sorry. RNB7 and Thumb2. Now Thumb2 is really, really great because the instruction sizes are both 16 and 32 bit, and you have full access to the floating point registers. So if you need to use a 32 bit instruction to get all the hardware capabilities, then we'll use it. And so because of this we actually turn it on all of the time. It's what you get by default.
And in fact, the entire OS is compiled in Thumb2. That allows us to keep our code size down. We see anywhere from a 20 to 35% reduction in code size which, if you imagine if you have to read 20 or 30% less code on App Launch, that's going to be a nice performance win for App Launch and anything that you're doing in your app.
Caches will have a lot more instructions in them if your code size is small. So Thumb2 is really, really great. And in addition to that on the iPhone 3GS we actually have one more thing that you can do, and that's Neon Code. So Neon is a vector floating point.
Or a vector unit much like altavech in SSE. And Neon allows you to do some very, very fast vector math. And we have an assembly interface to that currently, and we'll work on improving that a little bit. But it's very, very fast for the applications that are appropriate for it. Not everybody will use that certainly, and many of you in the room won't. But it is available if you have really, really complicated math to do. One last bit on code size. We see this occasionally even in some of our own software before we release it.
And that's C++ exceptions and runtime type identification which are features of the C++ language that have some overhead to them and set X code size. So if you don't need these features and don't know that you need them, then you can turn them off. And they're off by default. So if your code size is large you may just want to doublecheck that those checkboxes aren't checked, if you don't use those features.
One little bit that's sort of a memory and a speed thing is properties. So properties can be declared atomic or nonatomic. Atomic gives you thread safe properties, but it gives you autoreleased objects. So it's not quite as fast and you'll use a little bit more memory usage. So it's sort of a best practices and we use this in our APIs.
We suggest using nonatomic when you can. But certainly if you're using threading you should use the atomic properties. So that's static memory. On to dynamic memory. The main thing about dynamic memory is you want to keep in memory what you need. Now memory optimize. Keeping your memory footprint down doesn't mean that you never cache anything.
It means that you cache the right object. So if you're using something frequently in a scrolling list, you will probably want to cache some drawing objects. But you do need to architect your app in a way that you can release those objects if you need to. So on memory warnings you may have an image cache.
So for example, that application has a cache of thumbnails, and those thumbnails go away if you get a memory warning. So we keep them around as long as we can. And when the OS decides we're running out of memory, then we release them. So free the objects when you're done with them. And we have a couple of memory tools that you can use to memory object allocations.
Another little tip is try not to use autorelease when you don't need to. So autorelease is really, really great when you have an API boundary, and it doesn't really make sense to make whoever is calling the API own a particular object or deal with the memory management of that object. You just want to provide something back to them. But there are a lot of cases, for example in tight loops where you may be allocating a string with some sort of format. And then just stuffing it into some data structure or dictionary.
And so for those cases where you don't really need to autorelease, we suggest writing the one extra line of code. And rather than using the convenience methods that return autorelease methods, use the allocaknit variant of the initializers. And there are times, say, if you're parsing XML where you may have to use autoreleased objects. So some of our system APIs. And perhaps some of your APIs will sort of by their nature return autoreleased objects.
So for example, in a string as an API component separated by a string, which is really, really handy for breaking up strings if you're doing certain kinds of parsing, it returns autoreleased objects. And so for some of those cases where you want to use the really, really convenient method, you can actually create an autorelease tool so that all of that memory that you create will go away at the end. And the last thing that you definitely want to do is eliminate memory links.
So memory leaks are when you have a reference to your object in your code or. Sorry, when you have an object lying around in your heap that you don't actually have a reference to. So we have some tools and instruments that allow you to check for leaks conservatively.
So it will scan your process phase, figure out what objects are around that don't have any references to it, and report those to you. And it gives you the back trace of those. Memory leaks are usually pretty easy leaks. Pretty easy memory issues to fix. And so you should definitely run leaks and make sure you don't have anything major.
Because anything you leak is memory that your app can't use. And the one really, really important thing for iPhone, and this is both app architecture and implementation, is low memory warnings. Everybody should respond to low memory warnings because when the OS has gotten up to a point to where you're say 80 or 90% full, it will send you a memory warning and try to free up the memory that it can so that the rest of the OS can continue to run.
Because the OS knows if you keep using more and more memory there will be a point to where the OS itself can't run and it will have to terminate an app. So definitely respond to low memory warnings, and architect your apps so that you can. There are 3 APIs that we give you for this. The Application Delegate has an applicationDidReceiveMemoryWarning method.
UIView Controller subclasses have a DidReceiveMemoryWarning and it will do a lot of work for you. It will purge some offscreen views. And there are direct notifications, so if you have your image cache that doesn't really have a direct path from any of your app objects to this lower level data structure, you can register for the direct notifications.
So for low memory warnings, you want to make sure that your app is set up so that you can purge any offscreen views, most of which is done for you. Delete any cached objects that you have and get rid of any expensive resource files. To summarize, always use the right compiler settings for your app. Minimize your object lifetimes when you can. Eliminate any of the memory leaks that you have because that's memory that you can't use.
And architect your apps so that you can respond to low memory warnings and free up memory when it's really, really important. And if you want a lot more detail we actually have a session this afternoon that focuses exclusively on memory management. And we give you a whole lot of code examples that will show you how to translate all of these best practices and give you a lot more data into actually code.
So now let's move on to files and data. So we have just a couple of tips to give you about general file usage. The first is we have some APIs that allow you to load large resource files into memory, but without that memory sort of costing against your app in memory. And that's mmap and munmap. And we have NSData APIs that allow you to access this. So NSdatainitWithContentsofMappedFile creates a data with an mmapped backing.
So if you have giant 50 Megabyte file, say it's full of artwork or full of some other data, then when you use mmap for that the OS can decide when it wants to evict some of that memory. So if you have enough memory and you can keep half the file in memory, the OS will keep it in memory. But if your app uses more and more memory and the OS is running out of memory, it will throw that memory away.
So mmap is really great for large resource files, and we use it a lot in our system for our large resource files. And Core Data is a great addition to iPhone OS 3.0. And it's great from the memory management standpoint, and from the efficiency standpoint, and from how much code you have to write. So for large datasets definitely consider using Core Data. It has a few really nice features of it. It doesn't keep the entire dataset in memory.
So if you have some giant 3000 person or 3000 entry data file or database, it won't keep that in memory. You can specify well, I want to keep 200 in memory at any point in time. And it will just do a lot of work for you and do it on background threads so you don't have to write a whole bunch of complicated threading objects and threading code to get everything right. So it just handles a lot of the complexity for you, and it helps you have better memory management.
One thing that has come up in the last year is download performance where you really want, when your user clicks the Buy button on the app store you want the user to get that file as quickly as possible. And there are sort of 2 sides of this. One is anything over 10 Megs you can't download over 3G. So if you can keep your app below 10 Megs or if you're just on the edge you may want to see if you can get that down a little bit.
But one important thing is before you submit your app to the app store, just go and double check that you don't have any unnecessary files. And one example here is subversion directories where we actually had an app that was on the app store who had accidentally submitted some subversion directories, and the app ended up being 100 Megs more than what it actually needed to be, so avoid that if you can. One other thing that you can't do to improve performance, but you can do to make performance worse is backup performance.
All of our data in till the Documents is backed up. The idea is that users when they sync with iTunes we want them to have the last snapshot of their app. And so we back up everything in Documents. So for anything that's user data that they won't, that users won't want restored, if for whatever reason they need to restore their phone or they lose their phone, put that data in Documents. Use Library caches for any data that can't be required like downloaded images.
And then of course there's all the optimizations that we give you for free. The optimizing PNGs and we convert plist to binary because it's faster and uses less memory. So we have those optimizations that are built in for you. So that's a lot about performance. And now another critical aspect of performance is power and battery life, and to talk about that we have Peter Handel from the iPhone Power Team.
[ Clapping ]
Hi, everyone. My name's Peter Handel. I'm an iPhone Power Engineer. I've been doing that for almost 3 years now. I'd like to share with you some tips and tricks on how to improve the battery life of your application in 3 key areas. First off, when using the radio to send and receive data. Secondly, when using Core Locations to figure out where the device is. And finally, when using the CPU and GPU to get your work done and draw in the screen. First the radio. It's very expensive to send data over 3G.
Sending data over 3G is one of the most power intensive things you can do. This is exacerbated by the fact that the 3G networks keep the 3G radios on for a few seconds after data transmission. Therefore, if you were to send and receive even one byte of data every 5, 10 seconds or so, you'd be keeping those power hungry 3G radios on all the time.
And that's one of the quickest ways I know of how to drain your battery, so don't do that. So how can your application enjoy the wide availability and high speed of 3G and still have excellent battery life? Well, here's a few tips. First off, coalesce your data transfers into large chunks rather than transmitting a thin stream. This will allow those 3G radios to go idle more quickly. Also, minimize the amount of data that's transferred. Use a compact data format if you can.
Maybe even compress your data before transmission. So for the 3G radio chip, an idle chip is a green chip. From a power perspective, there are 2 main differences between Wi-Fi and 3G. First off, Wi-Fi uses roughly half the power of 3G. And I mean this can definitely change with network characteristics and stuff like that.
Also the Wi-Fi network will allow the Wi-Fi radios to go into a low power state immediately after transmitting data. Because of these differences your application might want to know whether or not it's on a cell network versus when it's on Wi-Fi. The way that you do this is you check a system configuration flag called KSE Network Reachability Flag as WM.
[ Silence ]
So where does 2G fit into this mix? Well, from a power perspective 2G falls kind of halfway between 3G and Wi-Fi. And 2G, like Wi-Fi, will, the 2G network will allow the 2G radios to go into that low power state immediately after data transfer. And that's the radios.
Next, Core Location. Judging from the number of apps on the app store that use Core Location, you guys love it and the customers love it too. If you haven't used it, Core Location can, with just a few lines of code, which I have up here, it can tell your device. It can tell your application where your device is to varying degrees of accuracy. However, be sure to only use the least amount of accuracy that you need, because the higher levels of accuracy use more battery power, especially the GPS.
For example, if you have an application that finds coffee shops, if you can figure out that you're here as the Moscone Center, that's probably good enough to know that there's a coffee shop right across the street. Next, the distance filter. This dictates how often you receive location change notifications.
In other words, they'll tell you when your device moves. Be sure to set it appropriately, however, because the default is to tell you every single movement. You can imagine this will result in a lot of unnecessary events which will use up a lot of CPU and therefore also battery life, so be sure to set this appropriately. Be sure to call StopUpdatingLocation as soon as you've reached your desired level of accuracy.
Also, note that Core Location will manage the GPS power for you. What this means is that it's OK to call StopUpdatingLocation, and then a few seconds later call StartUpdatingLocation again. For example, in your coffee shop finder application, if the user is on his way to the coffee shop and he decides to go into for example the Preferences pane of your application, it's OK at this point to call StopUpdatingLocation.
And then when the user goes out of the Preferences pane back to the Mac, for example, go ahead and call StartUpdatingLocation again. At this point Core Location will pick up right where it left off as if you had never called StopUpdatingLocation. So for the GPS chip, an idle chip is a green chip. And that's Core Location. Finally, the CPU and GPU.
Now, you might be wondering why are we talking about power and performance in the same presentation? Well, it turns out that when you optimize the performance you get better battery life thrown in for free. This is because fast code means less CPU time, which means less battery life use. So for the CPU, I'm sure you can guess what I'm going to say now.
An idle chip is a green chip. Now iPhone OS is an event-based operating system. Now in certain conditions you might be tempted to poll to check repeatedly to see when a condition has been met. Try to avoid this whenever possible. Instead, subscribe to an event whenever it's available. However sometimes there aren't events for some things, and so you have to poll.
So in this case use a timer with a low frequency. And instead of setting that frequency to something like every 30th of a second0 so you're continuously checking, try setting it to every 10th of second or maybe even every second to see if there's any user visible impact.
For example, if you want to use the accelerometer, you might be tempted to continually to check the accelerometer to see if it's being shook. Instead, use the shake API to subscribe to the event so you get notified when the device is being shook, and I have an example of that up here. For the CPU, try to be as bursty as possible. This will allow the CPU to enter an idle state that I've been talking about. Note that this may require you to completely restructure your code, or possibly even use a completely different algorithm.
How can you figure out when your code is being nice and bursty? Well, use the CPU Sampler Tool, which is part of Instruments to check the CPU level graphically. I have an example of that right up here. This is actually of a music playback from the iPhone. What we discovered is that when we coalesced, when we put together all of our decompression into large chunks. When we decompressed our audio into large chunks rather than just doing it a little bit at a time, our battery life improved quite a bit. This allows the CPU to idle for long periods between work.
So finally, procrastination. Who doesn't like to procrastinate? Because if you put it off long enough you just might not have to do it at all. So for example, we came across this game that saved its state every 30 seconds. You can imagine that's a lot of wasted CPU time, and also a lot of wasted battery life. Instead, maybe you could save the game state when the user exits the application. Or better yet, maybe your user's really into this game and he finishes it completely in one sitting.
In that case, there's no state to save at all. Oh, and there's just one more thing of course. When using the OpenGL ES or when using the GPU, be sure to pick a fixed frame rate rather than varying it up and down. Because if you vary your frame rate too much, it may give the appearance of dropped frames.
We're recommending you guys stick to about 30 frames per second. Also, if the frame hasn't changed, don't redraw the screen. For example, if you're writing a chess application and no pieces on the screen have moved, don't be redrawing the frame every 30th of a second because that can waste a lot of CPU and a lot of battery. So what have we learned today? Well, on the radios we know that data transmission is very expensive.
So we coalesce and compress our data as much as possible. And for Core Location we use the least amount of accuracy we need and we call StopUpdatingLocation as soon as we've reached the level of accuracy that we want. And with the CPU and GPU, we optimize for performance and get better battery life thrown in for free. We're bursty. We procrastinate as much as possible. And when using the GPU we use a fixed frame rate of 30 frames per second, and we don't unnecessarily redraw frames. So to summarize, an idle chip is a green chip.
[ Clapping ]
So we've learned a lot today about all sorts of aspects of performance. We learned about how to optimize your app to get the best scrolling performance on everything that we ship. We have Application Launch. Always make sure that your Application Launches loads as little as possible and launches quickly.