WWDC2016 Session 410

Transcript

[ Music ]
[ Applause ]
>> Good afternoon, everyone.
And welcome to Visual
Debugging with Xcode.
I'm Chris, and I work
on Xcode's debugger UI.
You know, debugging tools
have come a long way.
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.
[ Applause ]
Runtime issues elevates the
issues detected by the tools
at runtime to the
same status in the UI
as traditional build
time issues.
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.
And memory.
Our brand-new memory
graph debugger,
which we'll talk a lot more
about in a few moments,
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,
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.
[ Applause ]
Snapshots are now
up to 70% faster.
So you can go from
running your application
to debugging UI issues
quicker than ever before.
[ Applause ]
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
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.
[ Applause ]
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.
[ Applause ]
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
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.
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.
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.
[ Applause ]
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.
[ Applause ]
>> Thanks, Chris.
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.
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,
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.
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,
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
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.
[ Applause ]
>> 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
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?
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.
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
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.
Interesting.
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".
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.
[ Applause ]
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,
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
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
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.
[ Applause ]
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.
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,
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.
[ Applause ]