This workshop will be retired on May 31, 2020.
Allocations17:28 with Josh Timonen
The Allocations instrument captures information about memory allocation for an app. It records information from a single process only.
Let's open the sample app provided for this workshop. 0:00 We'll run it in the simulator and 0:04 you'll see that it's a pretty basic app with a table view and some images. 0:05 If you tap a row it pushes a new view controller with a big background image and 0:16 a number Back 0:20 in Xcode in the top menu select Xcode, open Developer Tool then Instruments, 0:27 the app opens and we're given a window to select a Profiling Template. 0:33 In the Windows top bar you could see this is where we select the device and 0:39 process for the template. 0:42 Let's select the iPhone 6s for the device and 0:44 our instruments demo app under install the apps for the target. 0:47 In the main window we see a bunch of different templates. 0:51 Just below the top bar, we can select Standard which shows all the templates, 0:55 Custom for any we've created and then Recent. 0:59 Blank allows us to start from scratch and 1:05 build up our own template out of existing or custom instruments. 1:07 Selecting each template updates the description in the bottom of the window. 1:11 For example, with Activity Monitor, 1:16 we see it monitors CPU, memory, disk, and network stats. 1:18 Not all of these are going to apply to your application and 1:22 some require you to build to an actual physical device rather than the simulator 1:25 if you're building an iOS app. 1:30 For example, if we select Energy Log, we see it gives us the messaging that 1:31 this instrument does not support the iOS simulator platform. 1:36 We need to build to an actual device to test this, 1:40 which makes sense since this deals with battery usage. 1:43 Something that isn't going to be applicable to the simulator. 1:45 Let's close this energy log window, 1:49 then go to File > New to get back to our template selection window. 1:51 For this workshop, we're going to focus on the Allocations template. 1:55 Let's select that, and hit Choose. 1:59 This opens up our main instruments window, which contains two instruments, 2:04 Allocations and VM tracker or virtual memory tracker. 2:08 The window is laid out very similarly to Xcode. 2:13 In the top left, we have a record button which starts an application session. 2:16 Next to it is the pause button and 2:21 then a drop down menu to select the application target. 2:23 Since we're running this in the simulator, we can click and 2:26 hold the iPhone 6S part of the drop-down, to change the device to another simulator, 2:29 or connected device, and then select any installed application on that device. 2:34 We'll of course be using our InstrumentsExample app, 2:39 which shows up in the installed apps list for 2:42 the simulator, since we've all ready built it through Xcode. 2:44 Next we see Run 0 of 0 which will increment each time we run the app and 2:47 then the time. 2:53 To the right there is an add plus button 2:54 which allows us to add new instruments to this template. 2:57 Next, we have toggles for showing the CPU instrument and 3:01 thread data which aren't going to apply to us here. 3:05 Finally, we have two toggles to expand or 3:08 collapse the detail window at the bottom or the inspectors window on the right. 3:10 We'll leave both of these visible. 3:15 When we hit Record, we see all sorts of data fill up in the detail window. 3:18 And we see the Allocation Instrument which now reads All Heap & Anonymous VM, 3:25 it's building a real-time graph of our memory footprint. 3:30 If we hit Stop, we see our charting in data collection stops. 3:37 Before we get any further, I want to show you a different way to get to 3:44 the Allocations Instrument from within Xcode. 3:48 This is the way I generally like to launch instruments to test an app I'm working on. 3:51 Let's switch back over to Xcode and run the app. 3:55 In the navigator window on the left side, 4:06 select the Debug navigator button which looks a bit like a hamburger. 4:08 Here we can see some basic stats for CPU, memory, disk, and network usage. 4:13 If we tap on each one, the main window is updated with more detailed information and 4:20 charting. 4:24 In the top right of the main window, there's a profile and 4:30 instruments button which transfers the running app process over to instruments, 4:33 opening a template with the relevant instruments. 4:38 Since we're looking at memory allocations, we'll select Memory in the left navigator 4:41 window then select Profile and Instruments in the top right of the main window. 4:46 Xcode presents a dialog asking if we want to transfer the process or 4:50 restart if we want to start fresh or cancel. 4:56 We'll hit transfer and we're able to get started in Instruments just like that. 4:59 I'm going to hit pause in Instruments for a second while we discuss a few things. 5:04 The profile Xcode uses here is actually the leaks template 5:09 which includes allocations and leaks together. 5:13 We'll focus on leaks in a separate workshop but it's nice to 5:16 use this path to get your app with the most recent updates over into Instruments. 5:19 We'll be using this launchpad throughout the workshop and 5:24 just focusing on the allocations data for now. 5:27 See the leaks workshop for more information on that instrument. 5:30 Sometimes when you first transfer your app you may not see data in 5:33 Instruments right away, you may have to do something in your app first like scroll or 5:37 take some other action. 5:41 Here we did start getting data right away but it's something to be aware of. 5:43 In the right inspector window select the Display Settings or 5:47 gear icon from its top menu. 5:50 We're going to be using this Mark Generation button while we run our app. 5:53 We're going to be examining the memory footprint as we go between our main view 5:57 controller and the detail view controller, 6:01 which is launched whenever we tap a row in the table view. 6:04 Our test will be tapping a row, then going back to the home screen. 6:07 We will Mark Generation before we start and after each repetition of our test. 6:11 Now start recording a new app session by pressing the record button. 6:16 The app will launch in the simulator, we'll scroll a bit and 6:21 we'll see that data is filling up the various windows and instruments. 6:24 After the app is finished launching, 6:29 we'll tap the Mark Generation button in the Inspector panel. 6:31 Let's pause for a second. 6:38 You'll see that Instruments places a red flag on the timeline, and 6:39 the details window shows Generation A, our Generation Snapshot. 6:43 This generation contains all of our launch data or everything up to this point and 6:48 we want to isolate that out of our testing. 6:53 Let's un-pause then tap a row in our app, which loads our detail view controller, 6:56 then tap the back button to return to our route view controller. 7:05 Once we've done that, will tap the Mark Generation button again. 7:10 Let's pause again for a second. 7:15 Now we'll repeat the process a couple of times, we'll tap on another row go back, 7:16 Mark Generation, then tap on another row then back and mark another generation. 7:22 So let's unpause and create those extra generations. 7:27 Let's hit stop and examine the data. 7:51 We can now see the details panel has listed our four generations A, B, C and D. 7:54 And the timeline chart above shows that our memory footprint was 8:01 steadily increasing with the each generation. 8:04 Looking at our generations in the details panel, we can see that generation A grew 8:11 2.99 megabytes and created around 1000 persisted memory allocations. 8:16 Since this was the launch of the app, we aren't going to be looking at this data. 8:22 We can see that generation B grew 8.9 megabytes, 8:26 generation C, 4.5, and generation D, about 3.9. 8:30 So each time we launch our Detail View Controller and then exit it, 8:35 we add around four megabytes or more to our memory footprint. 8:39 We know this shouldn't be the case, the app should release the memory for 8:42 that view controller once we're done with it. 8:46 To the left of each generation name is an expand button. 8:48 So let's expand our B Generation out to see all of the objects listed. 8:52 As you can see most of these are going to be Apple classes and 8:57 it's tough to find what you're looking for. 9:00 This is another good reason to always name your classes with a custom prefix. 9:02 We've prefixed our classes with TH such as THDetailViewController. 9:07 Up in the top right of the detail window is a search box, let's type in TH and 9:12 we'll get a much smaller subset of objects that have that string. 9:17 It looks like THDetailViewController is the only class listed that we created, so 9:21 we'll isolate it by typing the full class name in the search box. 9:26 We can see that generation B had one persistent instance of this class and 9:32 it was 816 bytes. 9:36 That's obviously not very much but 9:37 it's probably not the class itself that's causing the memory growth. 9:41 It's likely some of its properties like images or 9:44 other data objects it may be holding onto. 9:47 Now if we expand generations C and D, we can see that each of these generations 9:49 also created and persisted a single instance of this view controller. 9:54 Let's click the expand button to the left of the THDetailViewController class name, 10:00 which shows us the list of allocations identified by memory address. 10:05 Here we just show the single persistent instance from this generation. 10:09 Tap the arrow to the right of the memory address, 10:13 to see the method call that created this object. 10:15 We can see that it was THViewControllers, tableView:didSelectRowAtIndexPath method, 10:18 and we can double tap that road to see the code in our project where this took place. 10:24 Sure enough this is where we're creating our DetailViewController and 10:29 pushing it on to the navigation stack. 10:32 Well that all looks fine but the problem is that the View Controller is not being 10:35 released properly when it's finished. 10:39 Just below this pushViewController call, 10:41 we are adding the detailViewController object to an array called viewControllers, 10:43 which is a property on this THViewController. 10:48 So that's bad, each time this is getting pushed onto the nav stack, 10:51 it's also being added into this array. 10:55 And when we hit the back button in the DetailViewController, 10:58 when it would normally de-allocate the object, 11:02 it is still being held onto because it's also in this viewControllers array. 11:04 In the top right corner of the details window, let's hit the small Xcode icon 11:09 which will jump us back over into Xcode to this exact location. 11:13 Now let's delete this addObject call since this array wasn't doing anything anyway. 11:18 Let's run the app in Xcode then transfer it back to Instruments as we did before. 11:24 Now we'll Mark Generation to get a starting point then repeat the earlier 11:41 process marking a generation after each iteration of our test. 11:45 Let's pause, 12:23 let's put our THDetailViewController search parameter back into the search box. 12:25 Now if we expand each generation, we can see that there is nothing listed. 12:36 So the app is no longer persisting the view controller during each generation. 12:42 And if we look at the graph above, we can see three nice little camel humps. 12:46 Each time we left the Detail View Controller, 12:51 it released the ViewController object and its properties. 12:53 We can also see that the growth for each generation was much less. 12:57 Generation B grew 5.39 megabytes, 13:01 Generation C about 626 K, and Generation D, about 708K. 13:04 So it's not releasing all of the memory allocated between generations, and 13:11 it's possible there are other things that could be improved in the sample app. 13:15 But this is a significant improvement, and 13:19 our app isn't going to balloon in memory size as it was before. 13:21 Let's now look at a different way to examine the memory footprint of this view 13:24 controller by counting instances in memory of our DetailViewController. 13:28 First, we'll go back to our code base and 13:32 put that bad line of code back in after we push the viewController. 13:35 Self.viewControllers addObject:detailViewController, 13:44 then let's run the app and transfer to instruments as we've done before. 13:48 Let's pause for a second. 14:05 In the top of the Details window, make sure we show Details, 14:08 Statistics, Allocation Summary. 14:12 If not, select Statistics from the second drop down menu item. 14:14 Let's type in our THDetailViewController name into the Detail search box. 14:18 In the right Inspector window select the settings gear icon and 14:24 then under Allocation Lifespan make sure we have Created & Persistent selected. 14:28 Now let's unpause and in the simulator let's tap on a row and 14:35 load the DetailViewController. 14:39 We can see that it shows up in the detail window in instruments with 14:42 a persistent count of 1, if we leave the ViewController it stays. 14:46 We tap another row and now we have 2 and we do it again and we have 3. 14:52 As we deduced from our earlier test, 15:00 this view controller is not de-allocating as it should. 15:03 So let's go back to Xcode, remove that bad line, relaunch the app, and 15:06 transfer to instruments. 15:10 We re-enter our search term, ViewController name, 15:34 and start tapping through rows pushing and popping the view controller. 15:37 We can see that the object disappears as it should when we go back to the root view 16:02 controller. 16:06 We can also select All Allocations from Allocation Lifespan in the Inspector panel 16:07 which shows us the total the transience meaning objects that have been 16:12 de-allocated, and persistent, object's still in memory. 16:17 Let's pause for a second. 16:21 Here we can see that we had zero persistent objects but 16:23 four have been allocated. 16:26 If we un-pause then tap back and forth in the simulator a few more times, 16:27 we can see the total count rising and the persistent count correctly going to zero 16:32 as we land back on the main view controller. 16:36 Great, in this workshop, we got our feet wet with instruments and 16:49 learned how to inspect our memory footprint. 16:53 We debugged a case where memory wasn't being released and used generations 16:56 on our instrument timeline to create test iterations, and figure out how much memory 17:01 was being created and persisted during each iteration. 17:06 We learned how to transfer an app session from Xcode to Instruments and 17:10 finally, we learned how to monitor allocation counts of our objects 17:14 on the fly while we're running our app. 17:18 Be sure to check out our companion workshops on leaks and 17:21 time profiler if you enjoyed this workshop. 17:23 Thanks for watching. 17:26
You need to sign up for Treehouse in order to download course files.Sign up