Discover Xcode's enhancements for debugging autolayout issues at runtime. Learn how issues inside complex GKStateMachine objects can be easily debugged at runtime. Gain insight into finding performance bottlenecks inside SpriteKit and SceneKit apps more easily with the enhanced FPS gauge. Understand how to fix leaked and abandoned memory in your app by inspecting the heap from within your typical Xcode debugging workflow.
It wasn't all that long ago when our debugging tools looked something like this.
A little while later, with advancements in UI, our debugging tools began to look more like this.
But fast-forward to today, and our debugging tools have become much more powerful while at the same time becoming much easier to use. A big part of this is because the tools are becoming more visual, which helps us to solve problems faster and more intuitively.
Today, my colleagues and I are going to tell you about the latest in Xcode's visual debugging tools.
First, a quick overview of what we'll cover.
We're going to tell you about a new feature of Xcode to be able to report on issues detected by the tools at runtime.
We're going to tell you about the latest enhancements that we've made to Xcode's view debugger and how we've made auto layout debugging easier than ever before.
We're going to tell you about a new feature to be able to visually debug state machines and enhancements we've made to the FPS performance gauge to help with debugging SpriteKit and SceneKit frame rate issues.
Finally, we're going to tell you about a new feature of Xcode -- a visual memory graph debugger.
Let's get started by talking about issues.
We all know that build time issues such as compiler warnings, errors, and static analysis issues are well-supported by Xcode's user interface similarly for our issues detected while testing.
However, the same can't be said for issues detected by our expanding suite of runtime debugging and analysis tools.
These tools have been left behind with their best option being to log their output to the console. Not the best experience.
We thought we could do much better.
In Xcode 8, we're introducing runtime issues.
Runtime issues elevates the issues detected by the tools at runtime to the same status in the UI as traditional build time issues.
The Activity Viewer will display this indicator when any runtime issues are detected, along with the number of issues reported.
We've enhanced the Issue Navigator with a new runtime scope. This separates the issues detected at runtime from the traditional build time issues such as compiler warnings, errors, and static analysis issues.
So what issues will you expect to see reported at runtime? In Xcode 8, we're tackling three areas.
The first -- threading issues.
Our brand-new Thread Sanitizer is able to detect threading issues in your application at runtime.
UI layout issues. We've expanded Xcode's view debugger to be able to automatically detect ambiguous layout issues in your app at runtime.
Our brand-new memory graph debugger, which we'll talk a lot more about in a few moments, is able to automatically detect leaked memory in your application at runtime.
So, as you heard about on Monday, Xcode's latest runtime sanitizer is the Thread Sanitizer.
Thread Sanitizer helps us to detect and better understand threading issues in your applications at runtime.
It can detect such issues as data races, uses of uninitialized mutexes, unlocks from the wrong thread, thread leaks, and unsafe calls in signal handlers.
If any of these issues are detected, they'll be reported as runtime issues.
Thread Sanitizer is a powerful new runtime analysis tool. You can learn all about it by watching the Thread Sanitizer and Static Analysis session.
View debugging is a great example of Xcode's debugging tools becoming more visual.
Only in the last couple of years for debugging UI issues, we've gone from having to read debug output in a console like this to this. Xcode's visual view debugger is a much better experience for debugging and understanding visual UI issues.
If you haven't used it before, while Xcode is running your application, just tap the Debug View Hierarchy button down in the Debug Bar.
Xcode will snapshot your application, snapshot your view hierarchy, and explode it out in an interactive 3D scene.
From there, you can inspect the structure of the view hierarchy in the 3D canvas and in the outline view. And you can inspect the properties of all of the views and constraints using the inspectors.
In Xcode 8, we've made view debugging -- can you guess? Better than ever.
Snapshots are now up to 70% faster. So you can go from running your application to debugging UI issues quicker than ever before.
The rendering of complex layouts and transformed views is much more accurate in Xcode 8. And speaking of accuracy, Xcode can now render blurred view, such as visual effect views with high fidelity in the canvas.
So what you see in Xcode's view debugger more accurately reflects what you see on device. You'll see the blur rendering improvement land in beta 2.
We've added conveniences, such as being able to jump directly to the source code from a view class. Just tap the Jump button in the Object Inspector.
And navigator filtering is much more powerful, too. You can filter by any text in a label or text in a button's title.
Or you can filter by class names, and that will include super class names. For example, if you filter by UI label, you'll get back all of the subclasses of UI label in your view hierarchy.
You can even filter by memory address. So you can quickly find that particular view just by knowing its address and memory.
We've got some great improvements for auto layout debugging as well.
We show many more properties related to auto layout in the inspectors, and constraints are better represented in the canvas.
We now render badges on constraints to represent inequality or aspect ratio relationships. And we render non-required constraints with dash lines so you can easily differentiate between required and non-required constraints in the canvas.
But my favorite new feature for auto layout debugging is in conjunction with runtime issues.
Xcode is now able to automatically detect ambiguous layout issues in your view hierarchy at runtime.
So how does this work? While snapshotting your view hierarchy, Xcode will inspect every single view and is able to accurately determine if any of those views have ambiguous layouts, along with the reason for the ambiguity.
If any layout issues are detected, they'll be reported as runtime issues.
So you'll see them indicated in the Activity Viewer, and you'll see them listed in the Issue Navigator under runtime.
Furthermore, the view hierarchy outline will badge any views that have layout issues, so you can easily spot them in the context of the whole view hierarchy.
For a selected view, the Size Inspector will contain details of any layout issues, along with all of the constraints that participated in the layout for that view.
We're really excited about Xcode's new ability to be able to automatically detect ambiguous layout issues at runtime. And I'd love to give you a demo.
So I have an iPhone here, and I've got it connected to this Mac. We've got Xcode up and running a project called DemoBots. That's one of our sample code projects that we've updated to Swift 3 this year.
On the right, I'm just using QuickTime Player to stream the device's screen back to the desktop so we can all see it. Our team was tasked with adding an in-game instruction manual to DemoBots, so we implemented a How To Play screen.
However, we had some issues that we found before the session. So now is a great opportunity to debug those issues.
I'll tap How To Play. And this is our How To Play screen.
Not so great is it? Obviously, we have some issues, so let's take a look. We can see the DemoBots logo at the back, and there's some sort of mangled text rendered on top. So we need to debug this. Where do we start? I'll give you some clues.
DemoBots is a little arcade game written in SpriteKit, but this screen has been laid out using UIKit and auto layout. So a good place to start is to look under the hood at the structure of the view hierarchy and all the layouts. So let's do that now.
If I return to Xcode, down on the bottom, we can use the Debug View Hierarchy button, which I'll click now. And that pauses the application and snapshots the entire view hierarchy.
In the editor, we now get back an accurate representation of what we were seeing on screen.
And then to look under the hood at the structure, all we need to do is drag in the canvas and we get the whole view hierarchy exploited out for us in the 3D view.
Here, we can see all of the views that make up this particular screen -- the window at the back, container views, visual effects view, and then the views that make up the How To Play instructions. Let's zoom in on those.
We see the DemoBots logo at the back, and then a bunch of labels and images.
And we quickly get some insight by panning around the reason for the mess we see on screen. All of these views have been laid out one on top of the other. So we've got a layout issue.
Now our traditional workflow would be to inspect each of these views and their constraints and try and determine the reason for the layout issue. But in Xcode 8, we have some additional information available.
Notice up here in the Activity Viewer Xcode is reporting that we have some runtime issues.
We could click on that one, and that would take us to the issue navigator.
But you may also notice that over here on the left in the Debug Navigator, Xcode has badged some of the views for us. And that's telling us that these views have layout issues. So let's go straight to here.
If I select the first view, that highlights the view in the canvas for us. And let's open the Size Inspector for that view over here on the right.
Here, we can see under constraints the reason for the layout issue. This view has an ambiguous vertical position.
Now that means that auto layout doesn't have enough information to be able to unambiguously position this view in the vertical dimension.
Typically, constraints are missing.
Let's have a look at the next view.
That has the same issue -- vertical position is ambiguous.
And so does the next one. If I randomly click on a few more, it looks like they all have the same problem.
What I find curious is the very first sub-view in this list is not badged with an issue. So that could be a clue when we get to the layout code.
Let's go to that now.
If I select the Parent View, which has a class name of InstructionsLayoutView, this is the view responsible for laying out this How To Play screen.
So let's jump to the source code for that. An easy way to do that is to select the view. And over here on the right, we will find in the Object Inspector a button where we can jump directly to the source code for that view.
So we'll do that now. We'll close the Inspector to give us some room.
And let's take a look at the source for InstructionsLayoutView.
It starts by iterating over each of the parts of the instructions, which are just model objects which describe section headers, section paragraphs, and images.
And the code lays these out top-to-bottom.
So for each part, we fix the view and we add it to the view hierarchy.
Then we have some code for the horizontal layout. But we didn't notice any issues with horizontal layout, so let's skip over that to the vertical layout.
Here, we have two paths. The first is for the very first sub-view to constrain it to the top of the container.
Well, we already noted that the first sub-view wasn't reported as having any issues. So, that constraint we assume is set up correctly. So let's jump to the other side of the conditional.
Here, we're evaluating an optional previousPartView. And if we have it, we assign it to the local variable above you.
And then we can constrain each view to the view above it. It looks like this is the constraint that's missing.
So let's have a look at why this conditional is not becoming true.
If we select previousPartView -- we'll start with that one -- I'll use Command-A to select it for search and Command-F to get the Find Bar.
Let's look for instances of this variable.
We see it's defined at the top. It's set to "nil" before answering the fore loop. We evaluate it down here, and then we evaluate it again outside of the loop to handle the bottom constraint. But it looks like we're never assigning to it. So let's quickly fix that. So at the end of the loop, we can set previousPartView to the current partView. And then on each iteration, we'll have the previous view available, which will be assigned to above you. And we'll set up the constraint from a view to the one above it. Let's stop and rerun it and check our work. And I'll bring QuickTime to the front.
Just note how much information Xcode was able to give us before we even got to code.
We use view debugger to look under the hood at the structure and get some insight into the problem. But more than that, Xcode proactively told us about some layout issues in our application, which views had issues, and specifically what those issues were. So that gave us a lot of insight before we even got to the code, so we could quickly zero-in on the part of the code that we needed to.
Now that our application is running, I can tap How To Play. And there, it looks much better. We have a nice-looking instructions screen.
So Xcode is now able to report on issues detected at runtime using the same UI as we did for build time issues.
Xcode's view debugger has great enhancements this year, including faster snapshotting and more accurate rendering of complex layouts and blurred views.
We've got some great enhancement for auto layout debugging as well. In particular, Xcode is now able to automatically detect ambiguous layout issues at runtime.
Xcode's view debugger supports debugging UIs on Mac OS, iOS, and tvOS.
Give it a try with your projects, and we'd love to hear feedback on how it helps you with your debugging workflow.
And with that, I'd like to hand over to Tyler who's going to tell us about debugging state machines and frame rate issues. Thanks.
Today, I'll be showing you a new way you can enhance your debugging experience with the State Machine Quick Look, as well as some additions made inside the FPS performance gauge for Xcode 8.
Today, we have a number of useful Quick Looks already available inside of Xcode 7, and these provide you the ability to view a wide variety of objects live during your debugging. And you can even provide your own custom Quick Look to view objects within your app.
And now in Xcode 8, we're extending our built-in Quick Looks to include state machines. So let's first dive into what exactly a state machine is and how you could use it within your app.
So, many of you may already be familiar with GKStateMachine, which was part of our release of GameplayKit last year. And it's available on Mac OS, iOS, and tvOS.
State machines allow you to more easily define complex behavior by structuring it as a directed graph. And within a state machine, you provide discrete behavior for each state.
This could be something as simple as an animation that will play, or something more complex like an AI.
And then for each state, you define the conditions by which the state machine will transition from one state to another.
Once assembled, state machines can produce remarkably sophisticated behavior. However, they can quickly become difficult to visualize in code as they expand in complexity. And simple state machines can quickly evolve into far more elaborate ones.
In Xcode 7.3, our support for debugging state machines was limited to the current state, as well as its transitions. However, now in Xcode 8, we're able to visualize the entire state machine so that you can see exactly what's going on.
This is incredibly useful whether you're operating with very simple state machines or whether you're working with much more complex state machines.
With Quick Look, you're able to quickly debug potential issues and evaluate exactly what's happening within your state machines.
So, now, let's shift our focus over to performance.
Any time you're creating any sort of game or visual app, maintaining good performance is key.
And in Xcode 8, we've expanded the FPS performance gauge to help you with this. Many of you may already be familiar with parts of the FPS performance gauge from Xcode 7. And at the top of the report, you're provided a number of real-time statistics.
This includes your frame rate, which is the current number of frames being rendered per second, as well as your GPU utilization to see which parts of your GPU are being used the most, and your frame time for both the CPU and the GPU.
This helps indicate to you whether you may be CPU-bound or GPU-bound.
Now, in addition to real-time statistics, Xcode 8 now provides you a timeline history of your SpriteKit and SceneKit's frame time for both the CPU and the GPU. This is available on iOS and watchOS.
And what's great about this is that we breakdown your CPU frame time and its individual parts so you're able to see exactly how much time is spent rendering, or running your update loop, or evaluating actions and physics, and even how much time is spent idle.
And when your app is paused, you're able to scroll through the history of your app's performance so that you can see how it evolves as you progress through your app. And if there's a particular sample you're interested in, you can dive deeper to examine finer details on it and get some exact timings. So let's take a look at how we can use these within our app.
So now that we've addressed the layout issues that we've got. In our How To Play menu, let's go ahead and dive into the game itself.
The objective of our game is to convert all of the corrupted robots within our computer into good robots. And to do this, I have a beam that can zap them and reconfigure them into good robots. Now we see here I've already got a bad robot trying to come after me, so I'll use my beam to zap him.
So you see he's been converted to a good robot -- indicated with green.
But I see that we still have a part of our beam present above our character, and this shouldn't be the case.
Now since we're using a state machine to manage the behavior of our beam, this is a good candidate to use the State Machine Quick Look to figure out what's going on here.
So I'll go ahead and pause our app while I navigate to our BeamComponent.
Now our BeamComponent is where we create and update our state machine that manages the behavior of our beam. And I'll add a breakpoint here on our update loop and resume our game so that we hit that break point immediately.
So now that we're paused, I can go into debug area and find our instance of the state machine, and we can Quick Look it. And from here, we can see the entire state machine.
In blue, we see the current state that we're in, which is the BeamFiringState. And in gray, we see all of the additional states that comprise our state machine.
Now we also see the transitions between each of the states. And one thing that I've immediately noticed is that we have a number of transitions into our BeamFiringState but we have no transitions out of it. So this means, as soon as we get into our firing state, we have no way of leaving it. So let's go ahead and take a look at our BeamFiringState to see what's going on here.
So I'll remove our breakpoint and close our debug area and switch to our BeamFiringState.
Now we'll take a look at the update loop. And I see here we have some logic to transition both into the CoolingState as well as the IdleState.
But down here in our method where we're checking whether the state we're trying to transition to is valid, we're always returning false, which shouldn't be the case because we want to transition to either cooling or idle.
So I'll go ahead and fix that by checking whether the state we're trying to transition to is either of the two valid ones. And we'll go ahead and rerun our game and check whether this has addressed the issue that we were seeing.
Now when we transition to the FiringState and meet the conditions to exit it, we should properly be able to transition back into our IdleState.
So I'll jump back into the game and go ahead and fire at the corrupted robot to convert him into a good one. And we'll see the beam is no longer present above our player, so it looks like we've addressed the issue.
So now we've also noticed a performance issue within our game. We have a number of ground robots here on the bottom. And I notice that when we get attacked by them, our performance drops dramatically.
So I'll switch to our FPS performance gauge so that we can see our performance live while we're running.
And you can see on the right here we indicate to you your target frame time. And in our case, it's 16.6 milliseconds, which corresponds to maintaining a frame rate of 60 frames per second.
We can also see that a good amount of our time is spent rendering, as well as running our client update, and we've got a good amount of wiggle room of CPU idle time.
So I'll go into our game and move to the right here where we have an enemy robot, and I'll let him hit me so that we can try to reproduce this performance issue. So now I'll switch back to our performance gauge to see what's going on within our update loop.
And I'm noticing that quite a bit of time is spent evaluating actions. In fact, now our frame rate is dropping quite dramatically.
So I'll go ahead and pause our app so that we can take a closer look at what's going on.
Now that we're paused, I can scroll back in time within our frame breakdown to see our frame time previously within our app. In fact, here we can see the time that we were in the main menu where we spent a little bit of time rendering but most of it was spent idle, as well as the breakdown we were seeing within our game.
And now when we are seeing performance issues, I can click and hold to examine details for the performance issue we were seeing.
Here, I see we're getting 36.2 millisecond frame time, and 71% of that is spent evaluating actions. So what that tells me is there may be one of two issues present within our game.
We could have a single action within our scene that's taking an exceedingly large amount of time to evaluate, or we could have a large number of actions that's bottlenecking our update loop. So now I know where within our update loop we're having issues.
So we've seen how we can use the State Machine Quick Look to debug an issue we were seeing within our game, as well as how the FPS performance gauge can show us where exactly within our update loop we're having issues.
I'd like to now invite up Daniel Delwood who will show you a new memory graph debugger that we can use to determine where our issue with actions originates from so we can fix it.
Thank you, Tyler.
So I'm very excited to tell you about the new memory graph debugger in Xcode 8. And like the view debugger, it's a tool for understanding your applications better. So just as the view debugger understands your view hierarchy, the memory graph debugger helps you understand your memory and how it's referencing each other.
The core question though that it's trying to answer is - why does a certain object still exist on your heap? Now objects reference each other. And, you know, this is more and more a question of references and annotation these days in an automatic reference-guiding world.
So where can we go from having this problem of objects that we don't want, objects that are leaked, or abandoned? Well, there's some command-line tools that can help, such as Heap.
And what Heap does is it snapshots your process, looks through it for a summary of the different types and the counts of objects that are in your process. And you can even use the "addresses" flag to look for a specific type of object and get a list of those instances.
Once you have an instance you're interested in, leaks is where you go for the connectivity kind of information of, well, is it unreferenced? Is it leaked? Or is there some path from a global location in your application that goes all the way down to your object? Now, at any point in this investigation, you might need some further detail, such as the allocation stack trace. And, that, you can get with malloc-history.
And this is all not a very visual experience, and that's why we pulled all of these three tools into the IDE for the memory graph debugger. And so just a quick overview of, you know, how this is laid out.
On the left, the Navigator is where you get that heap-type of information to start your analysis.
The center editor area is where the connectivity information gets presented.
And on the right -- that's what we're showing you now -- the allocation stack trace via the Inspector.
So with that, I'd just like to jump right back into the demo where Tyler left off and see if we can take a look at those action problems that he was seeing. All right. So here we are with the FPS performance gauge, looking at, you know, actions that are probably at fault here in our application. So I'm just going to jump right in by choosing the Memory Graph Debugger button in the Debug Menu Bar.
And on the left here, the Navigator shows me my application with all of the different types that are allocated in it. And so they're broken down by a hierarchy of module and then type. And then under each of these, there's an instance.
And so, in this case, I'm kind of interested in searching my heap. And it's very easy to do. I can just type into the filter, and I'll look for actions.
So here we are. We've got types in SpriteKit. And we see that, yeah, we have a lot of actions -- 559. So it's probably that we have too many actions and not that we have some long-running single actions.
So let me select one of these objects. And the editor changes to show me the answer to the question of why this object is still around. In this case, it's showing me a root analysis graph which allows me to trace the object I've selected back to the left, back to the roots of my application.
So I can see that it's referenced by an SKC sequence by repeat. There's an array holding onto this. And I can even disclose further to see that here we've got an SKNode with some actions. So, OK, it's part of this SKNode actions list. I can click on this and try Quick Looking it. If I want to take some more looks at this action, I can select it and pull in the Inspector.
Now the Inspector shows me some memory details, such as the class name, the address, the hierarchy if it is a sub-class of some other objects.
What I'm interested in is where this action was created so I can jump to there. I can go ahead and collapse this stack trace and jump to my code. And here, we see I've got this function -- refreshHurtAction.
All right, so it's running a HurtAction. I can use the Quick Help to see that this action is added to the list of actions on the node.
But I actually wanted to only have a single-player action and make sure that this was replacing my previous actions. So it's a pretty simple fix. I'm just going to use the withKey variant here and replace the player action.
And the Quick Help will show me that, yes, this is actually the one I was wanting. If an action using the same key is already running, it is removed before the action is added. Great. So that's a pretty simple way to jump to an investigation about a specific type. But one of the other things I noticed when I hit the Memory Graph Debugging button is that the Runtime Issues Navigator alerted me to some issues. So I can click on that. And now I'm taken to the new Runtime Issues Navigator which has a bunch of leaks that were reported in my application.
So I'll start out with a type that is defined in my module -- say, this LoadSceneOperation.
If I select it, now the graph isn't showing me that same style. It's showing me a reference cycle, which is because this is a leaked object. It's not reachable from those locations in my application. And I need to find out what objects in the leaked set are referencing each other.
So looking at this quickly, I have an operation with some internal state. It's referencing a completion block. And then this has some captures as part of that block that strongly referenced my LoadSceneOperation.
So if I click on the block, I can see the back trace and go immediately there. And here we are -- my LoadSceneOperation completion block. I'm even using a capture list for "unowned self".
But the graph showed me that "self" wasn't the problem. It was the LoadSceneOperation capturing itself right there within the block.
So it's a pretty easy fix to make. I just need to capture it unowned and I can get going again.
But, unfortunately, that's not quite the solution here. Because, since it's a completion block, my LoadSceneOperation is just about done. And so once it executes this block here, the LoadSceneOperation is going to end its lifecycle and it won't be around for much longer. This means that when I dispatch async back to the main queue, this LoadSceneOperation may no longer be valid and I'm going to get a crash.
So it just goes to show that these captures can be tricky at times and require a little bit of investigation. And hopefully the memory graph debugger will help you in your investigations as well.
So, let's talk a little bit more about leaked and abandoned memory. The memory graph debugger is a debugger mode, and so it pauses to inspect your target application. This is so your application doesn't keep going and changing its state, and you can get a consistent view of the world. It also lets you do things like Quick Look or PO different objects as you go through your investigation, and it's available on all of our platforms.
Now, as I showed in the demo, there's two different graph styles.
And the first one is that root paths graph style which shows you for referenced memory -- maybe you've abandoned it -- how are different roots in your application like globals and currently-running threads referencing that memory.
Now with the progressive disclosure model, it lets you work back from your objecst to different intermediate objects and find the reference that should no longer be there.
For unreferenced memory or leaked memory, that's when you see the cycles view. And the goal there is to help show you what is strongly referencing itself. And it'll let you figure out, again, a reference problem.
So, for stack logging integration that you saw in the Inspector, it's not quite free to record all of the mallocs and frees in your application. And so this is a diagnostic that you'll need to opt into.
Just going to the Scheme Editor and selecting MallocStackLogging in the Diagnostics Tab is enough to enable it. And it will record, again, all of the mallocs and frees to disk so you can look them up later.
But for memory graph debugging, you don't really need all of the malloc and frees. And previous lifetimes of a malloc block just aren't usually that useful.
So, new in our current OSs is a Live Allocations Only Mode. And so this has a lower overhead, and it also allows you to get this rich information while you're memory graph debugging.
So this will set the "lite" flag to MallocStackLogging in target environment.
So one other thing that you may enjoy about memory graph debugging is that we've introduced a .memgraph file format. Now, sometimes you'll be debugging an issue and you won't have the time to really dive into it. And so you may want to save this off or have other engineers on your team take a look as well. So, from Xcode, you can actually go to the File menu and select Export Memory Graph. And what it'll do is save out all of the connectivity information and heap information, as well as some VM statistics about your application to a file. Then at some later time, you can just double-click, load that file in the Xcode, and take a look at the memory graph.
Now this does mean that there's no process in the debugger. So you can't get back traces, or Quick Look or PO the objects. But it's still a very powerful technique for an app analyzing after the fact.
Now if you want to build this into your continuous integration, we've actually got some options from the command line.
So you can just run leaks-outputGraph, pick a path, and save out a .memgraph file for later.
So leaks, vmmap, and heap have all been enhanced to read this.
All right, now the fun part. Let's talk about some usage tips here because this is all built on the leaks infrastructure.
Now what this means is the graph is conservative.
We're trying very, very hard not to report things as leaked when they're not. And so in that attempt to avoid false positives, there may be some extraneous references that you see in the graph.
Now these references will show up gray for being unknown. They may be valid references, they may not. We may not just have metadata available to the tool. And so take them with a grain of salt as you're reading these graphs.
Now one thing you can do to improve the accuracy is to enable Malloc Scribble, which is another diagnostic in this scheme. And this will mean that when allocation are free, it will write over the memory so that you don't have that uninitialized memory in that new block.
So for references that are known to be strong, these will show up as bold in the graph. And Swift 3 actually has a lot more reflection metadata available. And so I encourage you to use this because it definitely is a lot more accurate in terms of understanding captures and references.
And finally, I should put out that the memory graph debugger requires that you temporarily turn off sanitizers like Address Sanitizer or Thread Sanitizer.
So this is a lot of information.
Where is a great place to get started with your application? Well, validate your expectations. Are there more objects of a certain type than you expect? Are objects being deallocated when you expect? Are there any leaks in your types as well? Once you find an object that you're interested in investigating, then the goal is to find a path that shouldn't be there holding onto your object.
And two very common patterns that you'll find are strong captures from blocks and closures, or potentially even references upward in your graph that need to be marked as "weak" or "unowned".
So, that's a lot of information.
But I just want to thank you very much for listening to our information about new and improved visual tools in Xcode 8. We're really excited about things like the better visual debugging with the view debugger, with FPS gauge, and memory graph debugging. So go out, try them out in your app today, and solve a lot of issues.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.