Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: tech-talks-2011-extra-6
$eventId
ID of event: tech-talks
$eventContentId
ID of session without event part: 2011-extra-6
$eventShortId
Shortened ID of event: tech-talks
$year
Year of session: 2011
$extension
Extension of original filename: m4v
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2011] [Tech Talk China] Optimiz...

China Tech Talks #6

Optimizing App Performance with Instruments

2011 • 43:47

Great iOS apps delight users by performing flawlessly. From using memory conservatively, working with graphics efficiently, writing fast code, to avoiding blocking networking calls, learn tips and techniques to take your app to the next level.

Speaker: Michael Jurewitz

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it may have transcription errors.

Hi, I'm Michael Jurwitz, Apple's Developer Tools and Performance Evangelist. And today, we're here to talk about performance. Now, great apps have terrific performance. They delight users by being fast, being responsive to user input, and they use system resources efficiently. So today, we're going to look at some tips and best practices for helping your app perform great. So let's get started. So as I mentioned, we're here today to talk about performance. Now, performance is perhaps the most important thing for you to pay attention to in your application. So you might be wondering, what is performance? Well, performance is about being fast. It's about responding to your users quickly. It's also about being responsive, making sure that as they interact with the screen, your application is responding to their touch. And it's also about being efficient, making sure that you're using system resources wisely so that as you go to use something like memory, you're just using the memory you need, and you're not spiking all over place and causing the system to thrash. But beyond even that, it's about having a great impression with your users, because they notice these things. So you want to make sure that you put your best foot forward, so that you don't end up with reviews like this.

People saying that your app is slow and buggy, or that it crashes all the time, or even worse, one star. So today, we're going to focus on teaching you what you need to know to have a great performing application. We're going to take a look at how to test and measure performance, show you how to launch faster, and also show you how you can reduce your memory footprint. So first, let's take a look at measuring performance. Now the first question that probably comes to mind is, what are the things that you should be measuring? Well, in short, you need to be looking at everything. You need to be looking at processing time, how much time you're actually spending on the processor trying to get your work done.

You want to look at your memory usage to make sure that you're not using too much, that you aren't leaking memory. You want to look at your graphics performance and the correctness for how you're actually doing your drawing, and there are some important points to pay attention to here. You want to be looking at disk I/O so you know how you're interacting with the disk. And you want to look at battery life because your user wants to be able to continue to use their iPhone or iPad throughout the entire day. But to look at all this, you need some help.

And we've got an app to help you do that called Instruments. Now Instruments is your one-stop shop for all your performance needs. And it gives you a holistic view of everything that's going on in your application, from where you're spending that processing time, to how much memory you're using, and your graphics and battery life performance.

Now Instruments comes with a ton of templates by default. So let's take a look at a few of those. First, you've got the Time Profiler template. This provides you low-level statistical sampling of your application, so you know exactly where you're spending time and what algorithms you need to tune.

Next, you have the allocations template. The allocations template lets you look at the memory usage in your application to understand how many objects you're allocating, when you allocated them, and look at detailed information like even their retain counts. The core animation instrument lets you look at that valuable graphics correctness that I talked to you about before. It's a really, really powerful instrument. Finally, new in Xcode 4, you have the OpenGL ES Analyzer instrument, which gives you an in-depth look at how you're using OpenGL and can really help you tune that performance. It's really vital, especially if you're a game developer. her.

You also have the system activity template so you can see how you're interacting with the disk and exactly what's going on on the device. And finally, the power and battery life template lets you look at the power consumption of your application and overall see how it was draining power from the device so you can better fine tune things to reduce battery consumption. Now, when you start looking at what you need to do in measuring, the most important thing is don't guess. Now I'm going to say that again. Don't guess. You've got a powerful set of instruments at your fingertips to figure out exactly what you're doing. So even though you architected the code and you've got a great idea for what you think needs to be tuned, measure first. It's absolutely important that you measure. And most importantly, you want to focus on simple, repeatable, real-world actions.

Does your app have a table view that the user scrolls through? Tests scrolling through that. When they tap on an entry, do you load up additional data or go out to the network? Measure that experience specifically. Focus on these small repeatable actions that you can fine tune to make sure that they perform great.

And when you make a change, make sure that you go ahead and you retest, that you verify that you actually have made an improvement. Because the last thing you want to do is make a change that causes things to perform worse. And you want to keep iterating until it just feels right, until it feels great for you to use in your hand. Thank you.

Now when you're looking at something like instruments, there's a few things that you want to look for in particular in your application. With something like the allocations instrument, you want to make sure that you're not using tons of memory and that it's not growing monotonically over time. Your app should ebb and flow as it uses memory and it shouldn't just be marching upward constantly.

Using something like the leaks instrument, you should absolutely not have leaks in your app. You need to make sure that these get eliminated. And we'll talk about how to do that a little bit later. Thank you. And when you're using something like the time profiler instrument, you want to make sure that you're not pegging the CPU and using all of the available resources as much as possible. You want to make sure that you're tuning your algorithms to get their work done and then stop, so that you can do things like make sure that battery life lasts longer. Now when you're measuring performance, you want to make sure that you choose the right tuning environment. And it's important to remember that the simulator is a perfectly valid place to do a lot of performance testing. The simulator is fast, it's got rapid turnaround, and in this case of some instruments, it even has additional options that you can use to actually improve your analysis. So in the simulator, for example, you can use things like activity monitor or leaks or allocations. Generally speaking, the memory performance doesn't change between the simulator and the device, And so this is a perfect place to test that kind of correctness. But at the end of the day, the device is where you need to be doing your final testing. And it's also where you need to be doing the kind of testing like time profiler, core animation, or the OpenGL ES analyzer. Because those instruments and that performance really depends on the underlying device that you're running on. You need to know how the processor is going to behave, how the graphics processor is going to behave. and so you want to make sure that you're measuring where it matters. So the simulator is great for rapid turnaround. You've got additional memory debugging features, but generally speaking, it's unrealistic for things like timing, because of course the simulator is running on your laptop or desktop, which has got a really powerful Intel processor in it. And the device needs to be the final arbiter, so use it for all speed-related testing, and make sure that any memory fixes that you make in the simulator also bear out when you test on the device. All right, so let's talk about launching quickly. So now launching quickly is so important because it's the user's first impression of your application. From the moment they touch your app on the home screen and it begins to launch, they're immediately forming opinions about your application. This is your vital opportunity to communicate things about speed and the quality of your application. And you want to make sure that you're putting that best foot forward. So the common scenarios that we're going to take a look at in this section are both the non-multitasking launch of your app. So this is just when the user is launching your app from the first time. And we're also going to talk a little bit about the multitasking resume. This is when the user double taps the home button, presses your app, and you get reanimated from having been in the background. So when it comes to launching quickly, timing is everything. The OS is watching your app constantly, and slow performance will cause iOS to quit your application. Now, this seems kind of drastic, so why do we do something like this? Well, it's very important that if someone's using their iPhone or their iPad or their iPod Touch, that the system remain responsive.

We want to make sure that they can continue to use the device for what it's intended for. And if your application hangs and stops being responsive, then for all we know, the application may never recover. And so we need to make sure that the user can continue to use that device. So the timings we give you are rather generous, but you need to be aware of them so that you don't run up against them. So for example, for launch time, you have 20 seconds from the time your app launches to the time you've done executing application did finish launching with options and loaded your first view to be able to start turning the run loop and accepting user input. If you take longer than 20 seconds, your application is going to get killed. When you're resuming from the background, you've got 10 seconds to come back. When you're being suspended into the background, you have 10 seconds to do any cleanup that you want.

And finally, of course, if you're just being quit by the user, you've got six seconds to clean everything up. And we also have Task Completion API as part of the new multitasking API in iOS 4, so that if you have a long-running task, like an upload that you need to finish, you can get up to 10 minutes to run in the background to be able to finish that kind of activity. Now, when you're measuring launch, you need to test with realistic data sets. This is absolutely important. Now, when all of us are doing development, it's really common for us to just have a blank set of data that we're working with because you've just built the app and pushed it off to the device. But if you've got the kind of app that expects to be collecting tens, hundreds, thousands, tens of thousands of pieces of data, and you've got algorithms that load all this stuff on startup, well, then you're going to go from having launched quickly to possibly having your app being killed on startup for your higher-end users. So make sure you're testing for those real-world scenarios. And you can do that by using the Time Profiler instrument.

Now the Time Profiler instrument is a statistical sampler. What it does is it collects backtraces from your application at regular intervals. And so multiple times a second, it's quickly coming in, figuring out where your application is executing, recording that data, and then bringing it all back together so that you can know exactly where you were spending time on average. Now in general, it's highly accurate and it's got very, very low overhead. So it's a great thing to do on the device to understand where you're spending time.

And when you're looking at this data, you want to understand better what the heavy algorithms are. Where are you doing that big crunching of data? And you also want to be looking for opportunities to defer work or to do things on demand. In fact, there are a few rules to live by when it comes to launching quickly. You want to be asking yourself these questions every time you think about different things that you need to do on application startup. So the first question to ask yourself is, is it essential for loading the most basic main UI? And if the answer to that question is no, then you want to defer it. So anything you're doing in application did finish launching with options or view did load for your very, very first view, you want to make sure that it's integral to just getting that most basic UI on screen. Now, the next question to ask yourself is, does it involve parsing, loading data, or network activity? If the answer to any of that is true, then first of all, don't do it on the main thread. Now, let me say that again. Don't do it on the main thread. All of these different activities, parsing, loading data, network activity, could potentially take very, very long periods of time. And if you've only got 20 seconds for your application to be able to launch, then that might not be sufficient, especially if you're on something like a slow network connection or if loading that data is actually a huge data set that you're working with. So you want to be doing things like leaving yourself notes to the fact that you need to go and load this at a later point in time. You want to be deferring that work. And while you don't want to overuse it, it's really important to remember that progress indicators are your friend. It's a great way to communicate to the user that you're doing work on their behalf, you're trying to get things done without having to block the main thread while you wait for all this activity to come in. Thank you.

So, let's take a look at a demo for launching quickly with the Time Profiler instrument, so I can show you how to find this kind of activity. Okay, so let's take a look at helping your app launch even faster using instruments. So first, we're going to open up our project here in Xcode. And I'm going to go to Build and Run, so we can take a look at what it looks like.

We're building our application. It's being moved out to the device. And we can see it's taking a long time to launch. We're waiting. We're waiting. still waiting, and finally the content has come in. And as you see as we scroll through, we've just got a ton of content. If you look at the scroller on the right side, you can get an idea for just how much we have. So clearly this took a long time and we'd really like this to be faster. So let's look at how we can optimize this performance. So we'll go back to Xcode.

And now we're going to launch instruments. When we launch instruments, we're greeted with this series of templates for us to choose from. So we can take a look at the memory usage of our application, use the time profile of our instrument to understand where we're spending time, and even use things like UI automation or look at the energy diagnostics for our application. In this case, we're concerned about speed, so let's take a look at the Time Profiler application.

Now, I'm going to go ahead and choose Target and select our Launch Performance application and hit Record. And now our app is being launched on the device, and we're taking all these measurements. And as you can see, we're pegging the CPU, probably loading data or something. The app finally finished launching, and we see all this activity went away. So let's go ahead and stop this trace. Now, in instruments, if you see something like this for your application, this should be a big red flag. You don't want to see this kind of intensive computation right when you're trying to launch your application. So let's see if we can figure out exactly what's going on here. Now as you use your application again it's very important to make sure that you're just focusing on discrete user actions throughout your app. So if you take a trace that lasts a minute or two minutes you're gonna want to care about smaller portions of time in that. Things like when the user was scrolling, when you were loading data, when you are using the network, etc. So if you hold down the option key, you can select just a window of time in this trace document. So you're looking at just the data that matters.

Now if we go and we look at the left side of the screen here, there are a bunch of different options for filtering the data that you're looking at. By default, the time profiler comes with the call tree inverted. When I'm looking at information like this though, I like to un-invert the call tree and start at main and work my way down. Now a really useful option if you're just using Objective-C in your application is to check this "Show Objective-C Only". You can also check flatten recursion in case you have any recursive algorithms and you just want to flatten them into a single frame.

Now over here in the table view we've got the actual data from the run. And there's a few different things you can look at in this data as well. So if I control click on this menu you see there's a lot of different stuff we can pay attention to. In particular I want to look at self percentage. Now we've got running time which shows a percentage and self percentage. So what's the difference here?

Well, in the case of running time, running time is going to show you the total percentage of time that was spent in this method and everything else that it called through to. Thank you. In the case of self-percentage, what you're looking at was the time that was spent just in that method itself, so not the things that it called through to, like frameworks or other methods.

Now this is particularly important because you're going to be able to use this information to know do you need to optimize how you're using the frameworks and perhaps use them better or use different ones to get better speed or do you need to tune that method itself to help it go faster.

Now from here the important part is just to start stepping down the call tree. And you kind of want to be a detective and just follow this large percentage. So we keep going down. We see we're still at 96.3%. We're in UI application, handle event with new event.

We keep going, we've got more framework code that we're working with. We're continuing to follow this percentage. And finally, we've hit the launch performance app delegate, application did finish launching with options. Now, if you ever do a trace and you see this amount of time spent in application did finish launching with options, again, this should be a big red flag. You absolutely don't want to be spending this amount of time in your own code in that method. Your whole goal at this stage should be to kick off your application, to get it running, to do bookkeeping and take notes for yourself about different state that you need to set up, but you shouldn't be blocking while you do all this state loading at this point in time. So let's see exactly where we're spending time here.

If I turn this down, we see we've got our SONS view controller calling a method called parse. If I didn't know this code, this would already be another red flag saying, uh-oh, am I parsing and loading data on the main thread as part of launching my app? Sure enough, we've got this RSS parser here, and we're telling it to start, and it looks like it's just parsing through XML. Worse yet, we've got this download and parse method, so it sounds like we may actually even be doing some network activity as part of that. And sure enough, we get into framework code with NSXML parser, where we're actually parsing this XML from this feed that's come in. Now, if you're looking at your code and trying to figure out exactly where you're doing this, it's actually really easy in Instruments to see that. We can just double-click on the method itself, and we actually jump to the source, so you can see exactly where you are doing this work. And if you want to go to Xcode and be able to make a fix, perhaps change how you're doing this, you just press the little Xcode button and now you're back in your project. And so at this point, you want to be paying attention to this method or even just how you've architected this loading and try to make changes so that you're not loading this data on the main thread and really that you're just avoiding this parsing in general.

Alright, so remember, the system will terminate slow applications. You want to make sure that you collect data with Time Profiler. You want to make sure that you defer and otherwise just try to do less work. Avoid blocking the main thread at all costs. And finally, optimize, optimize, optimize. Be using Time Profiler to figure out exactly what you need to improve in your application.

Okay, so that's launching quickly. Now let's take a look at reducing memory footprint. Now, there's a lot of things going on in the device at one time. For instance, you've got 12 megabytes of memory that's going to the graphics processor. You've got a good 36 megabytes that are used by the kernel, it's wired memory. Approximately another 10 or so that are used by the different daemons on the phone. Springboard, the actual home screen, takes up another 8. And then you have the processes that actually make iPhone and iPod Touch what it is. You've got the phone process, the mail process, iPod, Safari. All of those have a memory impact. So it's important to understand that there are other things happening on the device at the same time that your application is executing.

Now iOS is a powerful operating system, but it's not a desktop OS. So you've got limited memory to work with. At the end of the day, this is a embedded device that you're working with. There's virtual memory, but there's no swap file. So while you're not sharing memory with different processes, all of the memory that you're using has to stay physically resident in the RAM on the device.

Now to help you work in these situations, we also have low memory notifications that are sent to every process on the device to make sure that you can work together to try to free up resources as they're needed. So as you can see in the chart, there is a steady upward march in terms of the amount of memory that's available, but the system is going to be using some of this memory for itself as well. So you need to make sure that you're architecting your apps to use as little as possible. And again, iOS has no swap file. All of that memory has to stay resident. And under sufficient amounts of memory pressure, the OS will actually begin terminating applications. Let's take a look at how that happens. So we have this system of low memory warnings. And as we look at memory increasing over time, there's a few things that happen.

First of all, a warning will get sent out. When apps get a chance to receive this notification, and basically it says, hey, we're getting a little low on memory. Please free up anything you can to try to help the system out. If memory though continues to grow over time, the system is gonna take a few more steps.

First of all, it's gonna cause background apps to exit. And it's gonna send another warning to the frontmost app, again, so that you can try to make sure that you're reducing your consumption. If memory usage though continues to march upward after that, eventually we reach a point where the system actually has to exit the frontmost application. And again, this is all about maintaining the overall stability of the device. Now typically, we see apps get quit for a few specific reasons. One of those is memory spikes, and it's important to understand this because this can actually impact even the performance of your application. So if you're moving along and you happen to have some algorithm that allocates a bunch of memory really quickly and causes a spike in usage, we can actually evict some of the memory in your app to disk. Typically speaking, this is things like the actual executable code of your application. The problem with that is that once we need to actually figure out what your app needs to execute, that has to be read back into memory, and that takes time for that to happen. This can lead to slowness in your application and so you really want to try to avoid these spikes if at all possible. Now the device is constantly watching memory pressure through a process called Jetsam. And Jetsam's job basically is to terminate apps with excessive memory usage. And this is even more critical now with multitasking in iOS 4. Now, if you're an app that goes into the background, it's important to note that if you can reduce your memory usage as much as possible, we'll actually be able to preserve you longer in the background. So if you think about it, when we start looking at apps that are in the background, we're going to look for the apps that are using the most memory first, because quitting those applications means more resources back for the system. So if you're using just a few megabytes, then you're pretty safe. So the name of the game is to stay safe and stay low. Now when you're reducing your memory footprint, you want to focus on three main areas. You want to focus on memory spikes, memory leaks, and also something that we call abandoned memory. So let's talk about spikes first. Now memory spikes are brief but sharply higher memory usage. Typically we see these kinds of things when you're processing large quantities of data. you're pulling down an XML file or loading some database from disk, and instead of just grabbing part of it, you're loading the entire thing at once. And so the best way to avoid these kind of spikes is to break this work into independent batches so you're only doing part of it at the same time. We also see these kinds of spikes when people are working with auto-released objects. Now this is kind of interesting and this leads to another point because you really want to try to minimize your use of auto-release. Now, typically speaking, a lot of people think that auto-release is used to avoid worrying about retain-release. Well, that is absolutely not the case. So please, please, please do not think about it that way. Auto-release is used for methods to return objects to who called them without actually retaining that object. So now when you auto release an object, what's actually happening is that that object is being added to what's called an NS auto release pool. And that pool is actually doing bookkeeping to understand that which objects it holds on to and which ones it needs to actually send a release to in the future. Now typically in the future means at the next turn of the run loop. But one of the ways that you can reduce memory spikes if you're using a lot of API that uses auto-release is to wrap that auto-release usage in a nested auto-release pool. So let's take a look at an example of a nested auto-release pool. First of all, we've just got some code here where essentially we've read in a file, we're iterating over it, and we're doing some processing with it. Now as we go through this while and this for loop, we're going to slowly start accumulating objects. Some arrays, some strings, some more arrays, some more strings, even more arrays, even more strings. And all these objects are going to go away eventually, but the problem is since they're auto-released, they're all building up until we're done executing what we're doing right now. and that might mean that we end up with too much memory usage in the short term. So you really want to try to reduce this. So how can you do that? Well, by simply adding an auto-release pool to surround this code, we can go ahead and make sure that these auto-released objects go away sooner. So it's very simple. You simply declare a pointer to an NSAutoReleasePool. Here we've called it pool. alloc and init that NSAutoReleasePool. And then at the very, very end of this code, but still inside the while, we simply call pool drain. And what this does, it'll make sure that all of these auto-released objects that were allocated between these two lines get cleaned up. So now we've got an array with some strings. We add a few more, and then they go away again. We allocate some, and they go away again. And so we're reducing that spike, and you're having more even memory usage over time. All right, next, let's take a look at leaks. Now, leaks are allocated memory that's no longer accessible. And so this is something where you've allocated a pointer, you've retained it, and then at some point you lost a reference to it. You assigned some new object into that pointer, but you didn't actually remember to release it.

And so that object is still sitting in memory, and you have no way to actually tell it to go away. And if your app has a lot of these time, then what you're doing is you're slowly suffocating yourself and taking away that vital memory that you need to use in order for your app to run. So you can find these kinds of things using the leaks instrument, and this will examine the heap for leaked memory. Now most commonly this ends up being unbalanced retain releases or something where you forgot to release a property's original value when you were setting it in something like a NIT. And we'll actually take a look later at how you can track down these issues and correct them.

Finally, the last category is abandoned memory. Now abandoned memory is sort of a special kind of leak. This is memory that is accessible, but just in the own usage of your application, you never actually end up using it again. So the classic example might be a cache that you're keeping around, that you only are putting objects into, and you're not actually referencing it for the information that you're caching. And so you end up just sort of slowly accumulating this memory over time. And again, just like with a leak, you're slowly suffocating yourself. And so you can use the allocations instrument with this brand new heap shot feature to be able to use your application and try to spot this kind of heap growth so that you can go ahead and reduce that and make sure that you're not using too much memory. Now as you're going along, you want to make sure that you don't ignore low memory warnings. These are really, really key to making sure that your app and the rest of the system continues to run great. So what should you do if you get a memory warning? Well, first of all, you want to make sure that you release any objects that can be easily reconstructed. You want to release any cached objects, things that you can read off disk again or that you really don't need. You were just keeping around in your caches. And you want to make sure that you unload cached resource files.

So say an image that you can just go and read off disk again easily, especially if that image is not currently on screen. So when you receive these low memory warnings, you want to make sure that you take action. But most importantly, don't ask the user to do anything. There's nothing they can do. This is something that you as a developer need to deal with in your application. It's not like they can use your application more conservatively or somehow change what they're trying to do. Now, when it comes to responding to low memory warnings, there's a bunch of different places where you can do it. First of all, UI view controller subclasses can implement viewDidUnload, and as we'll see in a minute, this is actually very necessary. In your app delegate, you can implement applicationDidReceiveMemoryWarning so that you can unload any resources at an app delegate level. And if you have any other objects throughout your application that want to know when a memory warning has come in, so they can do things like clean up caches, then they can register for UI application did receive memory warning notification and trigger that behavior that way. Now, UI view controllers will try to unload any off-screen views when a memory warning comes in. And they do this through calling viewDidUnload. But they need some help releasing views that you've retained in instance variables.

So let's take a look at an example of what's going on. So let's say we've got our application here that's got this Compose View Controller currently on the navigation stack. We tap on the photo and now we've got this Photo View Controller on the stack as we're looking at this photo. And at this point a low memory notification comes in. So what's going to happen is that the Compose View Controller is going to notice that it's off screen and it's going to release its reference to the UI view that it has. But if you've got IB outlets pointing to other views in that hierarchy, those are going to keep those views alive. So typically in your header you might have something that looks like this. You've got a property declaration.

You'll notice that property declaration is declared retain, which is how you declare IB outlets. And all of these are essentially going to have those views then retained, and they're not going to go away. So you want to make sure that you implement viewDidUnload so that you're going through, you're setting each of these outlets to nil, and then calling super viewDidUnload. And this is going to help that ComposeViewController to be able to get rid of that off-screen view hierarchy so that now it releases its view, viewDidUnload gets called, and you release your outlets, and that memory goes away. And the best thing about memory warnings is that you can actually simulate them yourself in the simulator. So if you go to the hardware menu, you'll notice there's an item called simulate memory warning. This is a great way for you to verify that the classes that you have in your application are responding just the way you think they should to memory warnings. Now when it comes to multitasking, again, the key is to make sure that you're surviving in the background. And it's also important to know that low memory notifications are not sent to backgrounded apps. Once your app is in the background and it's kind of been hibernated, that's it. The memory that you've already managed to get rid of as you were going into the background is your only opportunity. And so you need to make sure that you release as many reconstructable resources as possible. So when your AppDelegate's application didEnterBackground gets called, or after you receive the UI application didEnterBackground notification, use that short period of time that you have to reduce your memory usage as much as possible. Apps that use less memory in the background have a lower chance of being killed by the system. Thank you.

Okay, so now let's take a look at memory footprint and the allocations instrument to help you figure out how to improve the memory usage in your application. Okay, so let's see how we can get to the bottom of tough memory problems that you might run into when you're in development. So first, let's open up Xcode and take a look at our project. In this case, it's a project called Breadcrumbs. I'm going to go ahead and click Build and Run, and let's take a look at what this looks like. So we've built the app, it's being pushed out to the device, And as we can see, it's a pretty simple application. We can go scrolling through it. Notice we've got a bunch of table view data here. Pretty straightforward.

So let's go back to Xcode now and see how we can use instruments to look at the memory being used in this application. So we're going to bring up instruments, and we're going to start off with the leaks template. Now as you recall, leaks are something that you absolutely need to make sure that your app is not doing. So you want to make sure that you use this instrument to get rid of anything you see that involves leaked memory. So we're going to go to Choose Target and select our breadcrumbs application.

And now we're going to go ahead and hit record. And on the device we're now recording all of the allocation information from the device. As we scroll through the table view we see we can see all these allocations happening. And what leaks is doing is counting down to evaluating our process and looking for leaks.

We can see it's analyzed it. And sure enough, we found a bunch. We actually see we've found 50 different leaks just through scrolling through the table view. So obviously, this is a big issue because we've just scrolled. We've already leaked well over two kilobytes of data. If somebody's going to be using your app for a long period of time, that can really add up. And all of that is going to add up against your app and slowly suffocate you in the amount of memory that you have to work with. So, let's dig into this and see if we can figure out where these leaks are coming from.

Now most importantly you'll notice the leaks instrument is actually two instruments. It's leaks plus its allocations, so you can see all the different objects that are actually allocated. Now the allocations instrument has got a couple interesting switches that you'll want to know about. So the first is record reference counts. Now what this is going to do is pay attention to every single object in your application and look at its reference count as it gets sent different messages like retain, release, malic, free, and it's going to record all of this data for you. Now another one you should know about is called identify C++ objects. Now this can be important if you're an application that uses C++, because this is going to allow the allocations instrument to gain a whole lot more information about your objects in your application.

So let's go back to leaks and actually take a look at what was leaked. You'll notice it looks like it was a bunch of NSCF strings, which probably means it was just you using a bunch of NSStrings that ended up getting leaked. If we press this turndown, we can see all of the 50 different objects that were actually leaked. And in this case, we can see the responsible library, as well as the actual responsible call that led to that leak.

If we bring in the extended detail view, we can also see where the allocation actually happened. We get to see a back trace for the allocation. So let's just pick one of these objects and see if we can get a little bit more information. So we'll click this little arrow here and this will actually jump into detailed history for this particular pointer. We can see the point in time when the object was actually created and we see it's got a reference count of one. We see that it got retained with a reference count of 2, and then it got released and that went back down to a reference count of 1. Now at this point, apparently the leak happened. We lost a reference to this object, but it's still around, it still has a retained count. So let's see if we can figure out why that is. Now we notice from this first entry where this object was actually created, that this happened in our root view controller. So if we double click, we can actually see the code where the allocation took place.

we see that sure enough it's an NSString called SubtitleText. And we just want to kind of scan through here and see how we're using it. We notice that we're using it as part of another call here for creating this breadcrumb cell. It all looks pretty ordinary. And later we do in fact release it. So it looks like we're pretty balanced with our usage of allocating and releasing it in this method. So there must be something else going on here.

So let's go back to the pointer history. Now we notice here at some point this object gets retained again. And interestingly, our table view method is still on the back trace, but now we've got this setter, this set location string. So let's look at what's going on there.

Looks like when we call setLocation string, we pass in a string. It makes a copy of what gets passed in and just assigns that to our instance variable. Now, the only problem here is that we're not actually doing any bookkeeping to make sure that the old value that was in that instance variable is getting cleaned up. So this looks pretty sure like this is probably our leak. So let's click the Xcode button so we can jump to our editor.

And now we can go ahead and select this text. Let's paste in a new version here. And in this case what we're doing is we're checking to see if the instance variable and the input are different objects. If they are, we release the old value, we make a copy of the new value, and we update the instance variable. So now let's hit build and run to get this pushed out to our device. We're building the app now. There's a bunch of assets, so it takes a few moments.

Our app is running. We're actually going to stop it for a moment, just so we can go back and verify the leaks have gone away. So we hit Record in Instruments. And now we're taking a bunch of measurements. And if we go back to the device and start scrolling, we see that we've got a bunch of table view cells. And if we look back at instruments, it's about to analyze our process.

And sure enough, the leaks are gone. So this is how you can use the leaks instrument to get rid of those pesky leaks in your app and reduce your memory usage overall. So today, you learned how to use instruments to track down hotspots in your code, identify memory leaks, and use system resources efficiently. I hope you'll be able to use the information here to make your apps perform even better. I look forward to seeing what you have in store.