http://blog.csdn.net/woaifen3344/article/details/40748075
This is a blog post by iOS Tutorial Team member Matt Galloway, founder of SwipeStack, a mobile development team based in London, UK. You can also find me on Google+.
Learn how to troubleshoot and optimize your code with Xcode Instruments!
At this point in your iOS development career, you’ve probably written an app or two, and you are no doubt wondering what you can do to make your apps even better.
Besides improving your app by adding features, there is one thing that all good app developers should do… instrument their code!
This tutorial will show you how to use the most important features of the tool called Instruments that ships with Xcode. It allows you to check your code for performance issues, memory leaks, or other problems.
In this tutorial you’re going to learn:
- How to detect and fix memory management issues in your code using the allocations and leaks instruments, and
- How to determine hot-spots in your code using the time profiler instrument and how to make your code more efficient.
Note: This tutorial assumes that you are competent in Objective-C and iOS programming. If you are a complete beginner to iOS programming, you may wish to check out some of the other tutorials on this site. This tutorial makes use of a storyboard, so make sure you’re familiar with the concept; a good place to start is with the tutorial on this site.
This tutorial will be using Xcode 4.5, so make sure you’re fully updated to the latest version, which is available through the Mac App Store.
All set? Get ready to dive into the fascinating world of Instruments! :]
Getting Started
For this tutorial you won’t go through the process of creating an application from scratch; instead, a sample project has been provided for you. Your task is to go through the application and improve it using Instruments as your guide — very similar to how you would go about optimizing your own apps!
Download the starter project then unzip it and open it up in Xcode.
Build and run the app, perform a search, click the result, and you’ll see something like the following:
Browse through the application and check out the basic functions. As you can see, the core feature of the app is to search and display photos on Flickr. There is a search bar at the top of the app, and when you perform a search, a new row of results appear in the table.
This new row in the table displays the search term, and the number of results found in parentheses. If you tap on a cell, the search results expand and present you with another table, displaying the image titles along with preview images.
If you tap on one of the preview results, the app takes you to a full screen view of the image. From that view, you can then rotate the image if desired.
So far so good! :] You can see that the app works as designed. You might be tempted to think that once the UI looks great the app is ready for store submission. However, you’re about to see the value that using Instruments can add to your app.
The remainder of this tutorial will show you how to find and fix the issues that still exist in the app. You’ll see how Instruments can make debugging problems a whole lot easier ! :]
Time for Profiling
Lots of developers start out with a vague idea that their app should go fast – and it’s a worthy aim. Then they read about something called “premature optimisation” and wonder how this terrible thing that the greybeard programmers frown at might be avoided. In the worst case, the novice developers forget about optimisation altogether!
To some extent, you can leave optimisation out of your app development process; only ten years ago, mobile devices were incredibly limited, and even the use of floating point numbers was forbidden because it made code size larger and calculations move at glacial speed.
Now, you hold an incredible amount of power in your pocket, complete with 3D hardware good enough to beat the best desktop hardware of not so long ago. But you can’t always depend on hardware and processor speed to gloss over the inefficient bits in your app.
So what is “profiling”, anyway? Profiling is a means of measuring. The output of a profiling session provides insight into which parts of your code are used most often; in turn, that tells you which parts of the code you should try to improve.
You can spend a week fine-tuning an interesting algorithm, but if that code only occupies 0.5% of total execution time, nobody will ever notice the difference, no matter how much you improved it. If instead you spent the effort optimising the loop where your program spends 90% of its time, and made only a 10% improvement, chances are your update will attract five star reviews because it will feel so much faster!
The first lesson of optimisation is: find the right places to do it! :]
Premature optimisation, you will have guessed by now, is spending time optimising the bits that really don’t matter in the end.
The first instrument you’ll look at is the “Time Profiler”. At measured intervals, the execution of the program is halted, and a stack trace is performed on each running thread. Think of it as pressing the pause button in Xcode’s debugger.
Here’s a sneak preview of the Time Profiler:

This screen displays the call stack of each thread. Each level, or frame, as it is called, is a different method the program’s execution path has followed to arrive at the point where the CPU is currently executing the code – that is, frame 0.
The time spent in each method can then be determined from the number of times the profiler is stopped in each method.
For instance, if 100 samples are done at 1 millisecond intervals, and a particular method is found to be at the top of the stack in 10 samples, then you can deduce that approximately 10% of the total execution time — 10 milliseconds — was spent in that method. It’s a fairly crude approximation, but it works!
So without any further ado, time to get instrumenting!
From Xcode’s menu bar, select ProductProfile, or press ⌘I. This will build the app and launch Instruments. You will be greeted with a selection window that looks like this:
These are all different templates that come with Instruments.
Select the Time Profiler instrument and click Profile. This will launch the iOS simulator and start the app. You may be asked for your password to authorise Instruments to analyse other processes — fear not, it’s safe to provide here! :]
In the Instruments window, you can see the time counting up, and a little arrow moving from left to right above the graph in the center of the screen. This indicates that the app is running.
Now, start using the app. Search for some images, and drill down into one or more of the search results. You have probably noticed that going into a search result is tediously slow, and scrolling through a list of search results is also incredibly annoying – it’s a terribly clunky app!
Well, you’re in luck, for you’re about to embark on fixing it! However, you’re first going to get a quick run down on what you’re looking at in Instruments.
First, make sure the view selector in the toolbar has all three options selected, like so:
That will ensure that all panels are open. Now study the screenshot below and the explanation of each section beneath it:

- These are the recording controls. The middle red button will stop & start the app currently being profiled when it is clicked. This is actually stopping and starting the app — not pausing it.
- This is the run timer and run navigator. The timer counts how long the app being profiled has been running. The arrows move between runs. If you stop and then restart the app using the recording controls, that would start a new run. The display would then show “Run 2 of 2”, but you could get back to the data of the first run by first stopping your current run, then pressing the left arrow to go back.
- This is called a track. In the case of the time profiler template you selected, there’s just one instrument so there’s just one track. You’ll learn more about the specifics of the graph shown here later in the tutorial.
- This is the extended detail panel. In the case of the time profiler instrument, it’s used to show stack traces, as that is what Instruments is recording.
- This is the detail panel. It shows the main information about the particular instrument you’re using. In this case, it’s showing the methods which are “hottest” — that is, the ones that have used up the most CPU time.
If you click on the bar at the top which says “Call Tree” (the left hand one) and select “Sample List”, then you are presented with a different view of the data. This view is showing every single sample. Click on a few samples, and you’ll see the captured stack trace appear in the extended detail panel.
- This is the options panel. You’ll be learning more about these options shortly.
Now onto fixing the clunky UI! :]
Drilling Deep
Perform an image search, and drill into the results. I personally like searching for “dog”, but choose whatever you wish – you might be one of those cat people! :]
Now, scroll up and down the list a few times so that you’ve got a good amount of data in the time profiler. You should notice the numbers in the middle of the screen changing and the graph filling in; this tells you that CPU cycles are being used.
You really wouldn’t expect any UI to be as clunky as this; no table view is ready to ship until it scrolls like butter! To help pinpoint the problem, you need to set some options on the time profile perspective.
Under the Call Tree section on the left, select Separate by Thread, Invert Call Tree, Hide System Libraries andShow Obj-C Only. It will look like this:
Here’s what each option is doing to the data displayed in the table to the right:
- Separate by Thread: Each thread should be considered separately. This enables you to understand which threads are responsible for the greatest amount of CPU use.
- Invert Call Tree: With this option, the stack trace is considered from top to bottom. This means that you will see the methods in the table that would have been in frame 0 when the sample was taken. This is usually what you want, as you want to see the deepest methods where the CPU is spending its time.
- Hide Missing Symbols: If the dSYM file cannot be found for your app or a system framework, then instead of seeing method names (symbols) in the table, you’ll just see hex values. These correspond to the address of the instruction within the binary code. If this option is selected, then these are hidden, and only fully resolved symbols are displayed. This helps to declutter the data presented.
- Hide System Libraries: When this option is selected, only symbols from your own app are displayed. It’s often useful to select this option, since usually you only care about where the CPU is spending time in your own code – you can’t do much about how much CPU the system libraries are using!
- Show Obj-C Only: If this is selected, then only Objective-C methods are displayed, rather than any C or C++ functions. There are none in your program, but if you were looking at an OpenGL app, it might have some C++, for example.
- Flatten Recursion: This option treats recursive functions (ones which call themselves) as one entry in each stack trace, rather than multiple.
- Top Functions: Enabling this makes Instruments consider the total time spent in a function as the sum of the time directly within that function, as well as the time spent in functions called by that function. So if function A calls B, then A’s time is reported as the time spent in A PLUS the time spent in B. This can be really useful, as it lets you pick the largest time figure each time you descend into the call stack, zeroing in on your most time-consuming methods.
Although some values may be slightly different, the order of the entries should be similar to the table below once you have enabled the options above:
Well, that certainly doesn’t look too good. The vast majority of time is spent in the table cell that sets the photo. That shouldn’t come as too much of a shock to you, as the table scrolling was the clunkiest part of the UI, and that’s when the table cells are constantly being updated.
To find out more about what’s going on within that method, double click on the row. Doing so will bring up the following view:
Well that’s interesting, isn’t it! Almost three-quarters of the time spent in the setPhoto: method is spent creating the image data for the photo!
Now you can see what the problem is. NSData’s dataWithContentsOfURL blocks (that is, does not return) until the data has been downloaded. Since this request goes out to the internet to grab the data, each call could take up to a few seconds to return. This method is run on the main thread, and therefore the entire UI is blocked from updating whilst the image data is downloaded.
To solve this, a class has been provided called ImageCache which allows asynchronous downloading of images on a background thread. The code exists in the PhotoCell class.
You could now switch to Xcode and manually find the file, but Instruments has a handy “Open in Xcode” button right in front of your eyes. Locate it in the panel just above the code and click it:
There you go! Xcode opens up at exactly the right place. Boom!
Now, comment out the two lines which grab the NSData and set the image, and uncomment the block of code below. The setPhoto method will then look like this:
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">-</span> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(166,19,144)">void</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>setPhoto<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>FlickrPhoto <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">*</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>photo <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">{</span>
- _photo <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">=</span> photo;
- self.textLabel.text <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">=</span> photo.title;f
- <span style="border:0px; font-family:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(17,116,10)"><em>// NSData *imageData = [NSData dataWithContentsOfURL:_photo.thumbnailUrl];</em></span>
- <span style="border:0px; font-family:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(17,116,10)"><em>// self.imageView.image = [UIImage imageWithData:imageData];</em></span>
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">[</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">[</span>ImageCache sharedInstance<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">]</span> downloadImageAtURL<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span>_photo.thumbnailUrl
- completionHandler<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:^</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>UIImage <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">*</span>image<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">{</span>
- self.imageView.image <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">=</span> image;
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">[</span>self setNeedsLayout<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">]</span>;
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">}</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">]</span>;
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">}</span>
|
Re-run the app in Instruments by pressing ProductProfile (or ⌘I – remember, those shortcuts will really save you some time).
Notice that this time you are not asked for which instrument to use. This is because you still have a window open for this app, and Instruments assumes you want to run again with the same options.
Perform a few more searches, and notice that this time the UI is not quite so clunky! The images now load asynchronously and are cached in the background, so once they’ve been downloaded once they do not have to be downloaded again.
Looks great! Is it time to ship it? Not yet! :]
Allocations, Allocations, Allocations
The next instrument covered in this tutorial is the allocations instrument. This gives you detailed information about all the objects that are being created and the memory that backs them; it also shows you retained counts of each object.
The easiest way to start afresh with a new instruments profile is to just close Instruments and start again. Close Instruments, go back to Xcode and select ProductProfile again. Then select Allocations from the picker and clickProfile.
Instruments will open once again and you’ll be greeted with the following:

This time you’ll notice two tracks. One is called Allocations, and one is called VM Tracker. The Allocations track will be discussed in detail in this tutorial; the VM tracker is also very useful, but is a bit more complicated.
So what bug are you going to track down next? :]
There’s something hidden in the project that you probably don’t know is there. You’ve likely heard about memory leaks. But what you may not know is that there are actually two kinds of leaks.
The first is the true memory leak, where an object has not yet been deallocated, but is no longer referenced by anything. Therefore the memory can never be re-used.
The second kind of leak is a bit more tricky. It’s called “unbounded memory growth”. This happens where memory continues to be allocated and is never given a chance to be deallocated.
If this continues forever, then at some point the system’s memory will be filled and you’ll have a big memory problem on your hands. In iOS this means that the app will be killed by the system watch dog. This will not lead your app to five-star ratings! :]
Set up a scenario where you can detect unbounded memory growth. First, make 10 different searches in the app (but do not drill into the results yet). Make sure the searches have some results! Now let the app settle a bit by waiting a few seconds.
You should have noticed that the graph in the allocations track has been rising. This is telling you that memory is being allocated. It’s this feature that will guide you to finding unbounded memory growth.
What you’re going to perform is a “heap shot analysis”. To do this, press the button called “Mark Heap”. You’ll find the button on the left side of the detail panel:

Press it and you will see a red flag appear in the track, like so:

The purpose of heap shot analysis is to perform an action multiple times, and see if memory is growing in an unbounded fashion. Drill into a search, wait a few seconds for the images to load, and then go back to the main page. Then mark the heap again. Do this repeatedly for different searches.
After a drilling into a few searches, Instruments will look like this:

At this point, you should be getting suspicious. Notice how the blue graph is going up with each search that you drill into. If you continue this for all 10 searches, you’ll end up with a graph that looks like this:

Well, that certainly isn’t good. But wait, what about memory warnings? You know about those, right? Memory warnings are iOS’s way of telling an app that things are getting tight in the memory department, and you need to clear out some memory.
It’s possible that this growth is not just due to your app; it could be something in the depths of UIKit that’s holding onto memory. Give the system frameworks and your app a chance to clear their memory first before pointing a finger at either one.
Simulate a memory warning by selecting HardwareSimulate Memory Warning in the iOS simulator’s menu bar. You’ll notice that memory usage dips a little, but certainly not back to where it should be. So there’s still unbounded memory growth happening somewhere.
The reason for doing a heap shot after each iteration of drilling into a search is that you can see what memory has been allocated between each shot. Take a look in the detail panel and you’ll see a bunch of heap shots.
Hit Me With Your Best Shot
The first is the baseline shot. Open that up you’ll see all the objects that were allocated and still resident at the time that heap shot was taken. Subsequent heap shots will contain just the objects between the previous heap shot and the current one.
Look at the “Heap Growth” column and you’ll see that there is definitely growth occurring somewhere. Open up one of the heap shots and you’ll see this:

Wow, that’s a lot of objects! Where do you start?
The best thing to do is to look through the list for the classes that you use in your app directly. In this case, HTTPHeaderDict, CGRegion, CGPath, CFNumber, etc can all be ignored for now.
However, the one that stands out is UIImage, as that’s certainly something that is dealt with in your app. Click on the arrow on the left of UIImage to display the full list. Select one and look at the extended detail panel:
This shows you a stack trace at the point when this specific UIImage object was created. The parts of the stack trace in grey are in system libraries; the parts in black are in your app’s code. To get more context for this trace, double click on the only black frame that is in a method of the ImageCache class. This will take you to the code for that method, which looks like this:
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">-</span> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(166,19,144)">void</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>downloadImageAtURL<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span><a target="_blank" href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSURL_Class/" style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,104,55)"><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(64,0,128)">NSURL</span></a><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">*</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>url completionHandler<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>ImageCacheDownloadCompletionHandler<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>completion <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">{</span>
- UIImage <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">*</span>cachedImage <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">=</span> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">[</span>self imageForKey<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">[</span>url absoluteString<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">]</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">]</span>;
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(166,19,144)">if</span> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>cachedImage<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">{</span>
- completion<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>cachedImage<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>;
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">}</span> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(166,19,144)">else</span> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">{</span>
- dispatch_async<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>dispatch_get_global_queue<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>DISPATCH_QUEUE_PRIORITY_DEFAULT, <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(36,0,217)">0</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>, <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">^</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">{</span>
- <a target="_blank" href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSData_Class/" style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,104,55)"><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(64,0,128)">NSData</span></a> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">*</span>data <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">=</span> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">[</span><a target="_blank" href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSData_Class/" style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,104,55)"><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(64,0,128)">NSData</span></a> dataWithContentsOfURL<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span>url<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">]</span>;
- UIImage <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">*</span>image <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">=</span> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">[</span>UIImage imageWithData<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span>data<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">]</span>;
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">[</span>self setImage<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span>image forKey<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">[</span>url absoluteString<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">]</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">]</span>;
- dispatch_async<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>dispatch_get_main_queue<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>, <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">^</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">{</span>
- completion<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>image<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>;
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">}</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>;
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">}</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>;
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">}</span>
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">}</span>
|
Instruments is pretty useful, but it can help you no further in this case! You’re now going to have to work through the code yourself in order to understand what’s going on.
Take a look through the method above, and you’ll see it calling a method called setImage:forKey:. This method caches an image in case it is used again later on in the app. Ah! Well that certainly sounds like it could be a problem! :]
Take a look at the implementation of that method:
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">-</span> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(166,19,144)">void</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>setImage<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>UIImage<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">*</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>image forKey<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span><a target="_blank" href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/" style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,104,55)"><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(64,0,128)">NSString</span></a><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">*</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>key <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">{</span>
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">[</span>_cache setObject<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span>image forKey<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span>key<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">]</span>;
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">}</span>
|
This adds an image to a dictionary which is keyed on the URL the image came from. But if you look through the code, you’ll notice that the image is never cleared from that dictionary!
That’s where your unbounded memory growth is coming from! Everything is working as it should, but the app never removes things from the cache — it only ever adds them!
To fix the problem, all you need to do is have ImageCache listen to the memory warning notification that UIApplication fires. When ImageCache receives this, it must be a good citizen and clear its cache.
To make ImageCache listen to the notification, modify the init method to look like this:
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">-</span> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(166,19,144)">id</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>init <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">{</span>
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(166,19,144)">if</span> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>self <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">=</span> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">[</span>super init<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">]</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">{</span>
- _cache <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">=</span> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">[</span><a target="_blank" href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSMutableDictionary_Class/" style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,104,55)"><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(64,0,128)">NSMutableDictionary</span></a> new<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">]</span>;
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">[</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">[</span><a target="_blank" href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/" style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,104,55)"><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(64,0,128)">NSNotificationCenter</span></a> defaultCenter<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">]</span> addObserver<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span>self selector<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(166,19,144)">@selector</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>memoryWarning<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span> name<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span>UIApplicationDidReceiveMemoryWarningNotification object<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(166,19,144)">nil</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">]</span>;
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">}</span>
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(166,19,144)">return</span> self;
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">}</span>
|
This registers the UIApplicationDidReceiveMemoryWarningNotification to execute the memoryWarning:method. Now implement that method as below:
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">-</span> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(166,19,144)">void</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>memoryWarning<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span><a target="_blank" href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSNotification_Class/" style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,104,55)"><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(64,0,128)">NSNotification</span></a><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">*</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>note <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">{</span>
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">[</span>_cache removeAllObjects<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">]</span>;
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">}</span>
|
All that memoryWarning does above is remove all objects in the cache. This will ensure that nothing is holding onto the images any more and they will be deallocated.
To test this fix, fire up Instruments again (from Xcode with ⌘I) and repeat the steps you followed previously. Don’t forget to simulate a memory warning at the end!
Note: Make sure you launch from Xcode, triggering a build, rather than just hitting the red button in Instruments, in order to make sure you’re using the lastest code.
This time the heap shot analysis should look like this:

This time, the memory usage dropped dramatically after the memory warning. There’s still some memory growth overall, but nowhere near as much as before.
The reason there’s still some growth is really due to the system libraries, and there’s not much you can do about those. It appears that the system libraries are not freeing all of their memory, which may be by design or may be a bug. All you can do in your app is free up as much memory as possible, and you’ve already done that! :]
Well done! One more issue patched up! It must be time to ship by now! Oh, wait – there’s still the issue of the first type of leak that you haven’t yet addressed.
Call the Plumber — You’ve Got a Leak!
The next instrument you’ll be looking at is the Leaks instrument. This is used to find the first kind of leak mentioned earlier – the kind that occurs when an object is no longer referenced by anything, and eats up memory.
Detecting leaks is understandably a very complex affair, but the leaks tool remembers all objects that have been allocated and periodically scans through each object to determine if any cannot be accessed from any other object.
Close Instruments, go back to Xcode and select ProductProfile. Select the Leaks template and click Profile:
Instruments will fire up with 2 tracks – Allocations and Leaks. The allocations track is the same one that comes in the allocations template you used in the previous section.
You’ll only be concerned with the leaks track in this section, so click on that track to highlight it. Notice that the details you see in the rest of the Instruments window now change to reflect the state of that track.
In the detail panel, you’ll see that Automatic Snapshotting is turned on. This means that leaks are detected every so often automatically.
The interval between snapshots can be changed, but the default of 10 seconds is good enough for your purposes right now. You can force a snapshot at any time you wish by pressing the Snapshot Now button.
Back to your app! Perform a search and drill into the results. Then tap on one of the result preview rows to open the full screen viewer. Press the Rotate button at the top left, then press it again.
Go back to Instruments and wait for a moment. If you’ve done the above steps correctly, you’ll notice a leak has appeared! Your Instruments window will look like this:

Go back to the simulator and press Rotate a few more times. Then go back to Instruments and wait. Another few leaks should appear, looking like this:

Where’s the leak coming from? If the extended detail panel (on the right) isn’t open, remember the view selector control can get you there:
Open up the CGContext list in the extended detail panel. Select one of the CGContext elements in the list and look at the extended detail panel, which shows the stack trace that caused the object to be created, as below:

Once again, the frames that relate to your code are shown in black. Since there is only one, double click on it to see the code for that method.
The method in question is rotateTapped:, which is the handler called when the Rotate button is tapped. This method rotates the original image and creates a new image, as below:
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">-</span> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(166,19,144)">void</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>rotateTapped<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(166,19,144)">id</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>sender <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">{</span>
- UIImage <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">*</span>currentImage <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">=</span> _imageView.image;
- CGImageRef currentCGImage <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">=</span> currentImage.CGImage;
- CGSize originalSize <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">=</span> currentImage.size;
- CGSize rotatedSize <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">=</span> CGSizeMake<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>originalSize.height, originalSize.width<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>;
- CGContextRef context <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">=</span> CGBitmapContextCreate<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(166,19,144)">NULL</span>,
- rotatedSize.width,
- rotatedSize.height,
- CGImageGetBitsPerComponent<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>currentCGImage<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>,
- CGImageGetBitsPerPixel<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>currentCGImage<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">*</span> rotatedSize.width,
- CGImageGetColorSpace<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>currentCGImage<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>,
- CGImageGetBitmapInfo<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>currentCGImage<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>;
- CGContextTranslateCTM<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>context, rotatedSize.width, 0.0f<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>;
- CGContextRotateCTM<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>context, M_PI_2<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>;
- CGContextDrawImage<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>context, <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>CGRect<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span><span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">{</span>.origin<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">=</span>CGPointZero, .size<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">=</span>originalSize<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">}</span>, currentCGImage<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>;
- CGImageRef newCGImage <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">=</span> CGBitmapContextCreateImage<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>context<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>;
- UIImage <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">*</span>newImage <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">=</span> <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">[</span>UIImage imageWithCGImage<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">:</span>newCGImage<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">]</span>;
- self.imageView.image <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">=</span> newImage;
- <span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">}</span>
|
Again, Instruments can only give you a hint here as to where the problem lies; it can’t tell you exactly where the leak is. It’s only able to show you where the leaked object was created. It’s then your job to work out what the problem is! :]
You may think that ARC should have taken care of all the memory management, and that there couldn’t possibly be a leak in the code…right?
Recall that ARC only deals with Objective-C objects. It doesn’t manage the retain and release of CoreFoundation objects which are not Objective-C objects.
Ah, now it’s starting to become obvious what the problem is — the CGContextRef and CGImageRef objects are never released! To fix that, add the following two lines of code at the end of the rotateTapped method:
- CGImageRelease<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>newCGImage<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>;
- CGContextRelease<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">(</span>context<span style="border:0px; font-family:inherit; font-style:inherit; font-weight:inherit; margin:0px; outline:0px; padding:0px; vertical-align:baseline; color:rgb(0,34,0)">)</span>;
|
These two calls are required to balance out the retain counts of these two objects. The moral of this story is that you still need to know about reference counting — even if you use ARC in your project!
From within Xcode, use ⌘I again to build and run the app in Instruments.
Look at the app again in Instruments using the leaks instruments and see if that leak’s been fixed. If you’ve followed the above steps correctly, the leak should be gone!
Well done! SHIP IT! :]
- xcode的调试技巧
转自:http://www.cnblogs.com/daiweilai/p/4421340.html#biyouji 目录 前言逼优鸡知己知彼 百战不殆抽刀断Bug 普通操作 全局断点(Global ...
- Instruments 使用指南
Instruments 用户指南 http://cdn.cocimg.com/bbs/attachment/Fid_6/6_24457_90eabb4ed5b3863.pdf 原著:Apple Inc ...
- Instruments(性能调优 12.3)
Instruments Instruments是Xcode套件中没有被充分利用的一个工具.很多iOS开发者从没用过Instruments,或者只是用Leaks工具检测循环引用.实际上有很多Instru ...
- 使用Xcode进行调试
目录 知己知彼 百战不殆抽刀断Bug 普通操作 全局断点(Global BreakPoint) 条件断点(Condational Breakpoints)打印的艺术 NSLog 开启僵尸对象(Enab ...
- 苹果Instruments/Shark性能调试工具概述
在Mac OS X上你可以使用Gprof这样的UNIX工具用于测试程序性能.当然,Apple也有自己的Profiling Tools,用得比较多的是Shark.10.5里还引入了一个基于DTrace的 ...
- iOS-调试技巧
目录 前言逼优鸡知己知彼 百战不殆抽刀断Bug 普通操作 全局断点(Global BreakPoint) 条件断点(Condational Breakpoints)打印的艺术 NSLog 开启僵尸对象 ...
- iOS各种调试技巧豪华套餐
转载自http://www.cnblogs.com/daiweilai/p/4421340.html 目录 前言 逼优鸡 知己知彼 百战不殆 抽刀断Bug 普通操作 全局断点(Global Break ...
- iOS性能优化之内存管理:Analyze、Leaks、Allocations的使用和案例代码
最近接了个小任务,和公司的iOS小伙伴们分享下instruments的具体使用,于是有了这篇博客...性能优化是一个很大的话题,这里讨论的主要是内存泄露部分. 一. 一些相关概念 很多人应该比较了解这 ...
- Instruments_Automation使用入门
Instruments 是应用程序用来动态跟踪和分析 Mac OS X 和 iOS 代码的实用工具. 这是一个灵活而强大的工具,它让你可以跟踪一个或多个进程,并检查收集的数据. 这样,Instrume ...
随机推荐
- 创建控制器view的几种方式
1. 根据storyboard的描述创建 2. 通过xib的描述创建 3. 通过代码创建控制器的view self.window = [[UIWindow alloc] initWithFrame:[ ...
- [KMP][BZOJ1355][Baltic2009]Radio Transmission
题面 Description 给你一个字符串,它是由某个字符串不断自我连接形成的. 但是这个字符串是不确定的,现在只想知道它的最短长度是多少. Input 第一行给出字符串的长度,\(1 < L ...
- 15、python之导入模块
一.什么是模块? 模块本质是一个py文件,我们可以通过关键字import将py文件对象导入到当前名称空间. 二.导入模块 1.import module 2.from module import ob ...
- 笔记-scrapy-cookie
笔记-scrapy-cookie传递 1. scrapy cookie传递 1.1. 开始 与cookie相关的设置参数有两个: COOKIES_ENABLED 决定是否使用cooki ...
- 实时查询引擎 - Facebook Presto 介绍与应用
1. Presto 是什么 Facebook presto是什么,继Facebook创建了HIVE神器后的又一以SQL语言作为接口的分布式实时查询引擎,可以对PB级的数据进行快速的交互式查询.它支 ...
- JS:关于JS字面量及其容易忽略的12个小问题
简要 问题1:不能使用typeof判断一个null对象的数据类型 问题2:用双等号判断两个一样的变量,可能返回false 问题3:对于非十进制,如果超出了数值范围,则会报错 问题4:JS浮点数并不精确 ...
- 理解Queue队列中join()与task_done()的关系
在网上大多关于join()与task_done()的结束原话是这样的: Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号 ...
- 关于main与wmain函数
最近写一个控制台程序,并且希望该控制台程序运行时不显示控制台窗口,于是在程序include语句下面加入如下代码 #pragma comment (linker,"/subsystem:\&q ...
- mac虚拟机上(centos系统)设置联网第二种方式
这种方式简单,不容易出错,用的桥接的方式. 这样的安装的centos会得到ip地址 然后编辑一下网卡配置,使其变为静态得ip 输入命令 # vi /etc/sysconfig/network-scri ...
- Windows环境下,python webdriver环境搭建
最近刚开始学习selenium,这是我从虫师的<selenium2自动测试实战--基于Python语言>这本书上学到搭建环境的步骤,里面有加上我的一些总结,希望对大家有所帮助! 准备工 ...