WWDC2014 Session 410

Transcript

X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
>> Hello, everyone.
[ Applause ]
How's it going?
I heard the bash was
pretty amazing last night,
and I'm sure a lot
of you are hungover
so I'll speak really
softly to soothe it.
Nah, just kidding.
There's no mercy here.
Let's get right started.
We have a lot of great
content for you this morning.
You may have heard that Apple
released a new programming
language this week.
It's called Swift, apparently,
and it's the new language
for the Cocoa and
Cocoa Touch platforms.
It's a language we totally
love and it feels just awesome.
And what's even better is that
the tools you know and love,
the Xcode you know and love, the
source ADAFEr you know and love,
they all just feel great, and
the debugger you know and love,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
they all just feel great, and
the debugger you know and love,
LLDB, also feels just great.
The thing is, you've
heard about tools
that can help you explore Swift.
One of them, one of them that is
not usually thought of as a tool
for exploration is the debugger.
The debugger has
the unique property
that it can help you explore in
the context of your application.
Most of you probably do have
apps and those were written
in Objective-C and you might
have started off things
with features.
So, what you can do is you can
code some Swift inside your
application and you can use
the debugger to help you step
through your code, look
at your data and figure
out how your fancy new Swift
features are interacting
with your existing code base.
And of course, if it ever
happened that there were bugs
in your code, you could
actually use the debugger
to be productive to fix them.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to be productive to fix them.
We have a lot of stuff to cover.
We're going to talk about
some Swift types in LLDB.
We're going to talk
about optional types,
protocols and generics.
These are all categories of
types that Swift introduces.
What can you expect when
you're debugging your code
and you try to use this?
We're going to talk about
the mix and match situation
where you have some Objective-C
code and some Swift code
that are interoperating
together and you have
to debug the result of that.
We're going to cover stepping.
We're going to talk
about data formatters
and how you can expect data
formatters to work in Swift.
And we're going to start
with name uniqueness,
explain how Swift solves name
clashes in your, in your code
between different frameworks
and libraries and from there,
we're going to see how that very
same feature also helps make
debugging awesome.
Optional types.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Optional types.
Optional types introduce
a level of indirection.
Is it something inside
the optional or is it not?
The way I like to think of
optional types is a box.
I got a box and it says
there's a string here.
But I have to actually open
the box to look at the string
and see there's a string here,
actually there's nothing here.
In codes, that unwrapping the
box is something you have to do
and it's called interrupting
the optional.
LLDB helps you out.
When you're debugging,
it will implicitly,
automatically unwrap the
optional, open the box for you
and show you the contents.
And if there are no
contents, will just say nil.
How can we expect that to look?
Let's say we're writing code and
we create a bunch of variables.
We create a native
Swift string optional.
We create an NSRect as
bridge C struct optional
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We create an NSRect as
bridge C struct optional
and we create an NSURL
optional, and we choose
to not put anything
in the NSURL.
The variable views will
transparently unwrap for you.
Your string will show just
the contents of the string
literal you put in
the, in the optional.
The boxes open, oh,
there's a string.
LLDB will show you the string.
The same thing is
true for the NSRect.
Oh, that's actually
a rectangle in there.
I'll tell you about its
origin and its size.
And since the NSURL happens
to be nil, we'll just say nil.
There's a point when
this situation gets a
little trickier.
Since optionals are like boxes,
I can put boxes into boxes.
It probably happened to
all of us at some point.
We order something online
and the shipping company sends
us a really big, bulky box.
And then we open the
really big, bulky box
and there's a smaller
box inside.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and there's a smaller
box inside.
And then there's another
smaller box inside.
And then eventually
for all that packaging,
all we ordered is a tiny
little thing like a clicker.
In Swift, you can
do a similar thing
by having nested optionals.
In this example, we have
an optional of an optional
and what we say is in
the outermost optional
in the big box, we're
actually putting a smaller box,
but the smaller box is empty.
There's a fundamental
tension at work here.
If I am the debugger,
I have to decide what
to tell in this story.
I have to decide if
I want to tell you
that there's two boxes here
and one of them is full and one
of them is empty, or I can just
look inside the whole layering
of boxes and be the smart guy
that tells you, eh,
you know what?
There's nothing here.
Just forget it.
Just don't bother with it.
By default, LLDB chooses
to be that smart guy.
It looks inside all
the level of boxes
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It looks inside all
the level of boxes
and it tells you, you know what?
I looked. There's
really nothing here.
There's no string at
the end of the day.
But in some cases, I may
actually want to know
that there's a box with a box
and that the smaller box
is the one that's empty.
Maybe it matters
for my API contact
that that's the way things are.
For those cases, what you want
to be using is the
row display mode.
Let me give you a
little background here.
By default, LLDB has a
feature called data formatters.
The data formatter feature is
used throughout the debugger
when you're looking at data
to present you in no frills,
just get me the data that
matters in my contact situation.
But sometimes you need
to actually see the
underlying truth
without the debugger trying to
be smart about what to show you.
In those situations,
what you want
to use is this row display mode.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to use is this row display mode.
At the LLDB consult,
the row display,
the show me the real guts
of my objective memory
without formatting it in an
intelligent way is invoked
with the dash dash row, or for
short, dash upper case R option
to the expression command or
the frame variable command.
Some of you have probably
not used the frame variable
command before.
It's a little bit of
useful debugger trivia.
There are some cases where
maybe you're debugging a really,
really tricky situation and
you're trying to disturb
to preserve the state
of your app as little
as possible while
you try to figure
out this really weird situation.
In those cases, you want to look
at your data but you don't want
to be running code that
could change the state
of your app as you look at data.
You can use the frame
variable command
to say, "Show me on variable.
Show me a bunch of variables.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Show me a bunch of variables.
Show me all my locals"
without having to execute code.
And that is the frame
variable command.
If you also pass it the dash R
option, you get the row display.
You get things for what they
really are under the covers.
And if we do that to our
big box with smaller box
with nothing inside, we see that
the first thing LLDB tells us is
that yeah, there is a big box
and I see that there's something
in there, that's
what that Some means.
Now let me open this big box and
see exactly what is in there,
and we see that there is a
smaller box with nothing inside.
But it doesn't stop here.
We told the Swift compiler
that we wanted a box
that would fit a string, and
the Swift compiler made us a box
that would fit a string.
If there was a string here,
we would pretty much see that.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If there was a string here,
we would pretty much see that.
We would see the low-level,
no-frills representation
of a Swift string.
But there's no string
in this case.
So, everything in the storage
that the compiler reserved
from the string is zeroed
out because there
actually is nothing.
We've been talking
about optional types,
but we could also talk more
in general about types.
We could ask ourselves
the question, when we talk
about a type, what
are we talking about?
And that's a deep,
philosophical question.
That's a question with
potentially a lot of answers,
and I'm sure we would all
like to spend the rest
of the session going back and
forth exchanging definitions
of type, or you could just
trust me for a moment,
I could give you a definition
that works in the context
of the following slides,
and we could keep moving.
Let's just do that, will we?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I'm going to go with the idea
that a type is a classification
that tells me, given some
data, how can I expect
that data to be represented?
How can I expect to be
interacting with that data?
In a sense, I'm going to talk
about a type as if it was a hat
that a piece of data can wear.
A hat, data can wear a
little hat saying I'm an int,
and our piece of data
can say, I'm a string.
And the fancy guy down the
hall can say, I'm a UIView,
I'm really pretty, look at me.
The interesting thing
is unlike people's hats,
data can have multiple types.
The same piece of data can wear
multiple hats at the same time.
How is that possible?
There's a number of
ways to look at that.
One of the ways, the one
that is actually interesting
in the context about language
run times and debuggers is,
for instance, the static
dynamic type distinction.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
for instance, the static
dynamic type distinction.
We're all pretty familiar
I assume with the concept
of declaring a variable.
We've all done that at
one point or another.
One of the things we do when we
declare a variable is give it a
type, whether we do it
explicitly like in C
or Objective-C or we let
the Swift compiler infer
that for us.
We declare a variable and that
variable ends up having a type.
What does that type
do in the declaration?
In a sense, that type is
telling the compiler to keep us,
the code writers, honest.
When I, when I tell the compiler
that thing is an any object,
I'm, in a sense I'm telling
the compiler, "Please make sure
that whenever I use that object,
I play by the rules
of any object."
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And as long as I do that,
the compiler will be happy,
and if I break the contract,
when I told the compiler
if this is an any
object, keep me honest,
then the compiler will complain.
When I run time, however, things
become a little different.
Let's say I want to
get the hash code
for an NSURL object
or that object.
There's a lot of different ways
to get the hash code
for something.
How does the system know
that when I say URL.hash,
the implementation that I expect
is the one that will get called?
How does that work?
Well, that can't rely on the
fact that that's an any object,
because an any object could
potentially be almost anything.
At that point, I could just
as well choose randomly.
What happens is there's
a reliance
on the run time type
of the object.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The system looks at the type
that that object has while my
code is running at that moment,
and that's called the
run time dynamic type,
and it uses that information to
decide which hash gets called.
That's the magic of a
little mechanism called
dynamic dispatch.
So, we're here, and we
have our little URL object,
and we're trying
to call hash on it.
We said there's a hat on our URL
object that says "I'm a URL".
It turns out, that's
sort of true.
That object has ivars,
of course,
but it also has type
information.
For those of you
that use Objective-C,
that would be the iSA.
The iSA finder for that object
would be the type information.
One of the things that the
type information tells us is
which methods that this
objects type implement.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
which methods that this
objects type implement.
And in this case, one of the
ones that NSURL implements,
the example on the
slide, is hash.
So, we found it.
We know which hash to
call, and we're done.
Dynamic dispatch actually works.
What if we're trying to call
something that is not in the
in the [inaudible] methods that
that objects type implements?
Well, we could try
asking the base class.
If something had the NSURL hat,
it probably also has
the NSObject hat,
and that means we can say,
eh, that didn't work for you
as an NSURL, maybe that will
work for you as NSObject.
And that's what happens
in dynamic dispatch.
We go to the base type, we
try to find a method there,
and if that succeeds,
then we found it.
We can tell the method, hey,
here's an option for you,
please do your thing for me.
Thank you.
This same concept
is also interesting
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
This same concept
is also interesting
in the context of me debugging.
Let's say we have a
code example like that.
We have a base class,
we have a derived class,
which adds some information,
and we have a method.
We have a function that takes
an object of the base class.
We're telling the compiler in
that function, "Keep me honest
and make sure I only do things
that are okay for me to do
with the base class type."
But I can call in with an object
of derived class, can I not?
That's perfectly okay.
When I hit my break point, the
compiler has to keep me honest,
but the debugger doesn't
have to keep me that honest.
Actually, the very opposite.
I want the debugger to tell me
as much information as possible
about that argument, X.
I want the debugger to tell me
the dynamic type of X because on
that dynamic type
relies the fact
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that dynamic type
relies the fact
that I could have
more [inaudible] data,
that I could have
changes in behavior.
And indeed, that is exactly
what LLDB does by default.
It shows you the dynamic
type in your variables view.
Similar things apply
to protocols.
In Swift, protocols are types.
That means a number of things.
Among them, it means that
I can declare my variables
as of some protocol type.
It means that I can declare
functions taking their arguments
of protocol type or returning
object of protocol type.
By design, objects of
protocol type are limited.
They're constrained.
They may only let you play
by the rules of the protocol.
That's the whole point.
I want to make sure
that I only do what's OK
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I want to make sure
that I only do what's OK
to do on the protocol type.
But again, when I'm debugging,
I want to see the full truth.
I actually want to see
my implementing object,
and that's what LLDB
will show you.
Let's look at an example.
Let's say I'm writing
an app for a zoo system
and I'm prototyping things, so
I have a bunch of critters here.
I have a cat and a dog.
I don't have a dog collar
yet because it's a
prototype but I'll get there.
I have a function that takes
one of my creatures and asks it,
"Could you please speak
your voice for me?"
We can hit a breakpoint there.
We're in a similar
situation as before.
We declared something.
We gave something a static type
that is somehow abstract
compared to the real thing
that we're probably passing
at run time, and LLDB knows
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that we're probably passing
at run time, and LLDB knows
to figure out the dynamic
type information on our behalf
and show us that even though
we said we wanted just any
creature, in that
specific moment,
while our code is executing,
what we got here is a puppy.
And a very happy
puppy, for that matter.
For those of you that like the
LLDB console, you may be tempted
to try to reproduce this result.
Be careful.
By default, if you
just ask the debugger,
"Can you please show me this
variable or protocol type?"
the result you get may be
a little disappointing.
It will probably look like that.
What's going on?
What is that?
That doesn't look a
happy puppy at all.
What is happening is you're
seeing the static type.
We mentioned that the protocol
is somehow limiting object
by design.
It wants at the same
time to make sure
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It wants at the same
time to make sure
that you only do things that
are declared in a protocol
but you also get the dynamic
dispatching of this operations,
the real object that
implements that.
The result of that is what
is here on the screen.
What you want to do is
you want to tell LLDB,
"Please resolve the
dynamic type for me."
The way to do that is with
the -d flag to the expression
or frame variable command.
That lets the debugger
resolve dynamic types.
Now, there's two ways in
which you can ask the debugger
to resolve dynamic types.
There's a less restrictive
and a more restrictive way.
Sometimes in order to
figure out the dynamic type
of your objects, the debugger
might decide that it's best
to run some code
under the covers
to go ask the language run time,
"Can you help me out here?"
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to go ask the language run time,
"Can you help me out here?"
In that most liberal
settings, the run,
the allow run target are here
setting, you tell the debugger,
"OK, you can go run
some code off for me.
I don't think there's going to
be any problem if you do that.
Just let me know the dynamic
type when you're done, please."
Another setting is
Do Not Run Target.
In Do Not, in Do
Not Run Target mode,
you're telling the debugger,
"I'd really like to know
about the dynamic type of this
thing but I prefer you not
to run any code in my
[inaudible] process.
If it turns out that you have to
do that then just don't tell me
about the dynamic
type, that's OK.
I'll understand."
Limitation is that information
to resolve the type will
be passed along your data.
You will call a generic
function with an int and you'll,
your int data will be passed
through the function as well
as metadata that will tell
the language, "This is a,
this is an int you're
dealing with."
LLDB can use that
same information
to reconstruction the
meaning of your code.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to reconstruction the
meaning of your code.
How does that look like?
Let's say we have a protocol
for producing arbitrary things,
and then we have
a concrete class
that implements the
protocol, that conforms
to that protocol and
it produces ints.
And then we have a function that
says, "I can accept any producer
of things as long as what
they produce is int."
If I hit a break point, I expect
to see my generics resolved.
That is indeed what happens.
But how does it work?
Well, LLDB looks at your
function and it realizes
that your function takes
a generic argument, P.
So, LLDB has to look for
type information to resolve .
And it finds it.
When you debug generic code,
you're going to see a lot
of this $ with dot type
dot name special variables.
Those special variables carry
the generic type information.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Those special variables carry
the generic type information.
They're the Swift object
metadata for your generic type.
Armed with that knowledge,
when LLDB sees the argument
of type P, it knows to actually
use the generic type information
to resolve it to its
actual dynamic type.
We talked about a few
rules about how protocols
and generics behave in Swift.
Those are general rules
and for the most part,
they apply to debug builds.
A debug build of your
code is a very, very,
very literal translation
of the code you just wrote
into native executable code.
The fact that it's very literal
a translation is actually good
thing for debuggability
and that's why they're
called debug builds.
If my code is translated
literally as I'm debugging
through it, it's really easy for
me to see the correspondence.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
through it, it's really easy for
me to see the correspondence.
Evolution, evolutioning
machine code execution,
evolutioning source
code execution.
It's really easy for
the debugger to maintain
that correspondence
between what you wrote
and what is actually
going on on the bare,
on the bare metal,
on the hardware.
In an optimized build
of your code,
while maintaining
the same semantics,
the compiler is actually free
to shuffle things a little bit
behind your back and that means
that the literal evolution
of machine code will
not correspond anymore
to the same sequenced
evolution of your source code.
There will be steps, there will
be jumps, data will not be there
that it was supposed
to be there.
As a result of that,
the first rule
of debugging optimized
code is that you don't.
I'm sure it's a lesson
some of you
in the audience have
had to learn.
You probably have apps
and you probably get bugs
from those apps very,
very rarely, I'm sure.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But sometimes it happens, and
the first thing you should do
when you get one of those rare,
incoming bugs is reproduce it
in a debug build of your app.
Only if that doesn't work
then, sorry, tough luck.
You're going to have to
debug optimized code.
All the usual caveats
apply to Swift
and there's a couple new
ones that are specific
to things we covered
in the previous slides.
While in general type
metadata is passed along,
the Swift compiler is
free in optimized builds
to actually specialize
a way some types, some,
your generic functions
for some specific types.
Also, if the compiler can
understand what's going
on with protocols and
the concrete types
and implement them, it's
free to do devirtualization
to skip dynamic dispatch
and directly call
into the implementing object.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
into the implementing object.
Objective-C isn't
really going anywhere.
Some of you, we said, already
have apps and it's very likely
that those are written
in Objective-C.
But, even if you start
a brand new Swift app
for the first time today after
this session, you're going
to use Cocoa or Cocoa Touch.
You're going to import
foundation, import UIKit.
Those frameworks are
written in Objective-C.
That means wherever you
look around, there's going
to be Objective-C in the
picture, and you're going
to have to deal with
debugging mixed Swift
and Objective-C situations.
What can you expect
when that happens?
What can you expect
to seen in the LLDB,
in the Xcode variables view?
What can you expect as you try
to evaluate expressions
in the LLDB console?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to evaluate expressions
in the LLDB console?
What can you expect when
you try to PO your objects?
At the variables view, it goes
by what's called a
most native experience.
We'll show you data
in the language
in which the type
was first written.
In this case, we see a Swift
string and an NSstring side
by side, and the Swift string is
shown as a Swift string literal,
as you would type in
Swift source code.
The Objective-C string
literal is shown
as an Objective-C
string literal.
It's shown as if, as you
would type that same thing
in Objective-C source code.
In all cases, data
formatters will apply.
If I'm evaluating
expressions, however,
things become a little
more strongly separated.
Expressions see two
separate worlds.
Objects that exist in Swift
frames are all useable
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Objects that exist in Swift
frames are all useable
by Swift expressions, and
the same is true for objects
in Objective-C code frames.
Your results, your
result variables,
they get two separate
name spaces.
A little background on that.
When you type an LLDB
expression command,
the result of that
expression is stored away
in a debugger-generated
persistent variable,
which you're very welcome to
reuse in subsequent expressions.
The results of your Objective-C
expressions will get stored
in variables named $0,
$1, $2, you get the idea,
and the results of your Swift
expressions will be stored
in variables names $R0,
$R1, and you get the idea.
Let's see an example of how
this whole system works.
We're stopped in a Cocoa frame.
The F command tells,
tells us the frame
where we're currently stopped.
We type an Objective-C
expression because we're
in Objective-C frame,
just be self.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in Objective-C frame,
just be self.
And we get a variable $0
that stores away self.
Now, we step around a little bit
and we land in a Swift frame.
Now, we like to try and use that
$0 persistent variable and we
like to write an Objective-C
expression that involves it.
That's not going to fly so well.
Since we're in a Swift frame,
the Swift compiler is trying
to compile your Swift,
your expression
with the Swift syntax.
But that's not Swift syntax,
that's Objective-C syntax.
And so the compiler
gets really unhappy
and he mentions things like,
"anonymous closure argument
not contained in a closure."
OK, I must be doing
something wrong here.
Well, what's going
on is you're trying
to use the other language.
And there's a way for you to do
that but you have to tell LLDB
"Don't automatically infer
the language of my expressions
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
"Don't automatically infer
the language of my expressions
from the language from
the frame I stopped in.
Use the language I
tell you to use."
In this example, we're
using the -l, or the --
language flag, that
expression command.
And we're telling LLDB,
"Use the Objective C++
expression evaluator.
Use the Clang compiler
that is inside of you
to actually parse
that expression."
And then that works.
But there's a caveat.
There's always a caveat.
Your locals will
not be available.
Since you changed your
language, as we said before,
locals are not available.
PO is in a way similar to
the expression command,
but it actually, once you get
the result of your expression,
it goes back to that
most native experience
that the variable view lives by.
Swift objects will display
using data formatters.
Objective-C objects will display
using their description method,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
much like they did
from before Xcode 6.
That can get funny real quick.
I can have a Swift class
that inherits NSObject
and I can actually
override description
for that class in Swift.
But if I try to PO it in LLDB,
LLDB will not even look
at that description.
LLDB will use data formatters,
and that's what I'll get,
because that's a Swift object.
What if I actually wanted
to use my description?
What if I actually want to use,
see the Objective-C
side of things.
It turns out there is a way.
When there's a will
there's always a way.
I can start from there
and I guess I have
to write an expression,
and I guess I have
to write an Objective-C
expression since I'm trying
to get an Objective-C behavior.
Skimming for the help for PO,
an expression, I can discover
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Skimming for the help for PO,
an expression, I can discover
that the PO behavior is
actually triggered by a flag
to the expression command,
the dash uppercase O flag
for object description.
And so I can guess that I need
to write an Objective-C
expression
that gets an object's
description.
But now I changed language.
Now I can't use my
object local anymore.
I can resort to using
its address.
I know it lives somewhere
in memory and PO told me
where it lives in memory.
I can use that information to
go across the language barrier
and bring my object
along with me.
But, but I'm not using my
local type information anymore,
so that's a number.
For all Clang knows,
I'm asking it,
"Can you please show
me that number?"
I need to tell the compiler
that what I actually want
to see is not the
number OX 000 something.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I want to see the object
of that location in memory.
The simplest way to get
there, just [inaudible],
and after all this magic, your
result shows up, just like that.
Thank you [applause].
Thank you.
Let's very quickly step through
a couple stepping scenarios,
protocols and closures.
Let's say I set a break
point right where I'm trying
to use one of my creature
objects and I step in,
because I actually want to see
the implementation of that code
and step through it and
see what's going on.
It turns out that
that just works.
LLDB lands right where
you would expect.
But there's one extra
frame on the stack.
There's a frame called
protocol witness
for Creature.speak on the stack.
That frame is the protocol,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
That frame is the protocol,
is the protocol dynamic
dispatch frame.
It's the code that the Swift
run time uses between where I,
my code stops and when my code,
and when the call
code starts executing
to actually perform
dynamic dispatch.
LLDB automatically steps
through writing through my code.
And if I step out, the same
magic happens in reverse.
The protocol in this frame just
disappears and I get right back
into my code transparently.
And I have more good
news for you.
You can set break
points inside closures.
You can expect LLDB to leave
your breakpoints inside our
closure even if it's an
aligned anonymous closure.
And that's what you'll
see in the stack.
You'll see that since
your closure is aligned
and anonymous, it's called
closure number 1 in my call,
in my calling function.
You can also expect
to see your locals.
Even those, yes, even
those $ variables
that are automatically
generated by the compiler
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that are automatically
generated by the compiler
and you never declared.
Good news all over the board.
First topic.
Now, this topic is
really dear to my heart
because that's what I usually
work on, data formatters.
Data formatters are
a way in LLDB
to improve the way
your data is shown,
to hide implementation
details and only focus
on the core things that matter
to you when you're debugging.
Much like we do for
C++ and Objective-C.,
we automatically format
types in the Swift library.
You don't have to
worry about that.
That will happen
automatically for you.
But the good news is that
the mechanism is pluggable.
This is covered in great
detail in last year's session,
which you're welcome to watch
online or on the LLDB website.
We'll just quickly go
through an example to show
that you can roll your own Swift
formatters much like you could
in C++ and Objective-C.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Let's say we have a struct that
represents a person's address
and we try to PO my address
card represented in the struct.
That's not a horrible
display but it looks nothing
like an address would
look, right?
I want to make this
more envelope-like.
I want this to look
a little bit more
like I was writing my
address on an envelope.
I can do that with the
LLDB-type summary add command.
The type summary add command
is that LLDB's command to say,
"When you're showing me
a variable of this type,
here's the at-a-glance
information I'd
like to see represented."
And so we can tell LLDB, You
should use the variable's name.
You should use the
name of the person.
You should use the city
and separate those two
by new lines since you're at it.
Now, put another new line
in there for me, will you?
And write a zip code.
And then after a
comma and a space,
could you please put the state.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
could you please put the state.
This is the US address
format, basically.
And we're saying, Use that
for the address object.
Now, when we PO me again, we get
something that looks a lot more
like an actual address.
Data formatter's
mission accomplished.
I told you there's
always caveats.
There's a few more.
When you actually tell LLDB bind
this formatter to this type,
you have to use the
fully qualified name.
That includes the
name of the module.
We'll talk about
that in a little bit.
If you're writing
Python formatters,
you want to use
SBValue.GetSummary.
You want to ask objects
for a summary.
Even for things that in
C or Objective-C look
like basic types that have a
value, like an int or a float,
in Swift there's a little
more intricacy going
on under the hood.
So, what you want to
show, what you want
to ask is the object summary.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to ask is the object summary.
Caveat to the caveat,
except for enums.
When you have a Swift enum
and you want to figure
out which case is
selected, you ask the value.
Let's talk about
name uniqueness.
Let's say you, you guys are
writing an awesome Objective-C
app and there's a
really good framework
that would help make you
so much more productive.
It's Foo.framework, of course.
Foo.framework has developers
who have really good
taste in class naming.
Their taste in class naming
is so good that they came
up with a really
nice class name.
Unfortunately, these
guys have too good
of a taste of their own good.
A little while later,
the developers
of another great
framework, Bar.framework,
came up with the same
super-nice class name.
Now, the result of this
is not nice at all.
The result of this is undefined.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The result of this is undefined.
There's two frameworks with the
same class with the same name,
trying to coexist in the
same app at the same time.
That's not nice.
That's undefined.
You don't get to choose which
class gets actually loaded.
In Swift, that's
gone [applause].
Thank you.
Swift provides uniqueness
among function overloads
and among classes in
different frameworks.
The way to let the old is
goodness is a feature called
mangled names.
Some of you may come from C++
and may be familiar
with that already.
Let's talk about it.
There's two guys and they're
both writing Swift code.
They don't know about each
other but they both think
that my class is the best
name ever for a class.
I don't agree with that
but that's their choice.
They want to submit their
code to the Swift Compiler.
The first guy goes
on and does it.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The first guy goes
on and does it.
As a result, he gets a
compiled version of his code
where his class is actually
called something my module
my class.
We'll get back to
that in a second.
When the second guy does that,
it does that same thing,
it tries to compile.
His class actually gets called
something module 2 my class.
Now they don't clash anymore,
Module1MyClass, Module2MyClass.
There's a little price to pay.
Now, if I actually look at the
screen and see what the name
of the class became
at linkage time,
it's called underscore
TtC7Module17MyClass.
And that's just for
declaring a class.
Now, the world is actually
a scary place sometimes.
What if you were out
there in the wild roaming
through dark streets and out
of a little alley a mangled
name came right at you.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of a little alley a mangled
name came right at you.
That could happen
while you're, you know,
you're just sitting there
interface builder doing things
and oh, there's a
mangled name there.
Or, worse even, your
app just crashed.
Not all of you are going to
get crash reporter window.
You're also getting
mangles names in there.
It doesn't get much
worse than that,
except Swift demangled
comes to the rescue.
Swift demangled is a little
tool that ships with Xcode
that lets you pass as
input on the command line 1
or more Swift mangled names,
and it magically provides
you the demangled version.
So, fear no more encounters with
mangled names in dark alleys.
This is the magic of modules.
This is the magic of
modules in the context
of name uniqueness,
of avoiding clashes.
Modules can do a
lot more for us.
Modules actually make
debugging a lot more awesome.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Modules actually make
debugging a lot more awesome.
Why? How? Let's look at it.
I have source code for my app
written in Swift and I gave it
to the Swift Compiler.
The output of that process is
an app and module information.
Why is that so important,
the module information?
Well, it turns out that
much like it contains a copy
of Clang, LLDB contains a copy
of the Swift Compiler inside,
and that's used, of course,
as part of expression
evaluation for Swift.
But, there's a little
more to the story.
Now, when I try to
debug that app,
the copy of the compiler will
actually be able to infer,
to ingest the module information
for the compiled app.
Why is that so important?
Let me give you a little
perspective on what happens
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
when you compile an
app for debugging
and then you try to debug it.
When you tell the
compiler, any compiler,
let's go with Clang
in this example.
When you tell Clang, "Please
compile my app for debugging",
what Clang does it
ingests your code as usual.
It understands that your code
and gets get a mental
module of it in some sense.
Then you tell Clang,
"Give me information
to help me debug my app."
What that process does
is it generates DWARF.
DWARF is a format
used specifically
for containing debug
information.
So, Clang has an understanding
of the type system of your app.
It takes that understanding,
it translates it
into a different format, DWARF.
The debugger, LLDB, then
ingests that DWARF information
and it has to recreate
an understanding
of the type system of your app.
The way LLDB does that is
it generates Clang types
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The way LLDB does that is
it generates Clang types
out of DWARF.
Hold on a second.
So what you're telling
me here is basically
that Clang generates Clang
types out of my source code.
Then it translates
that into DWARF.
Then, LLDB consumes the DWARF
and generates Clang types back.
Why? Wouldn't it be really nice
if LLDB could directly
understand the compiler's notion
of types?
Wouldn't it be great
if we didn't have the
intermediate steps,
and the compiler parses your
source code, understands it.
It creates a representation
of the types,
it's own representation
of the types in your app,
and the debugger gets to use
that same representation?
That certainly seems nice.
It certainly seems
nice for us that have
to actually write the debugger.
We skip 1 step.
We can directly use the
compiler's notion of the truth.
It's also really nice for you,
the users of the debugger.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The reason, well, there's
a couple of reasons.
The obvious one is that
if we could do that,
there would be no potential
for loss of information
in the translation process.
We went, we would go
from source, Clang types,
DWARF Clang types, to source
types, types, one less step
where information can get lost.
That is what happens in Swift.
The Swift Compiler
generates a module
which is the compiler's
understanding of the truth
of your program at the
time it was being compiled.
LLDB's copy of the
Swift Compiler ingests
that module information
and can use it
to reproduce the type system
that the compiler was seeing
at the time your program
was being compiled.
There's no loss of
information and there's no need
for the intermediate step
of re-creating the types
from, back from DWARF.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of re-creating the types
from, back from DWARF.
There's one more advantage.
Some of you have
written C++ code
and you've probably
used generics.
For that situation, I'm
sure you will run into it.
You try to use some
generic function
and you actually didn't use
that in your source program
and now LLDB's complaining,
"You're trying
to use this function with
this really weird name
that is not present
in the target."
It turns out that that
won't happen in Swift.
Since generics are
actually types
that the compiler
understands natively
and since we have the compiler's
very own understanding
of the truth, now we get
every type and every function
through module information,
even those you did not use
in your source code,
and even generic ones.
That was a lot of
ground covered today.
If you remember one thing
from this session, well,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If you remember one thing
from this session, well,
there's always caveats.
But no, that's not the thing
I want you to remember.
You can choose your language and
LLDB will be there every step
of the way with helpful
investigation tools.
Whether you're, whether
you're using Use With Features
or you're debugging
your existing code base,
the helpful features in
the debugger will be there
to help you make
your app awesome.
We talked about a
bunch of topics.
We talked about Swift types,
stepping, data formatters
and modules, and probably
even more than that.
The important thing is your
feedback matters a lot to us.
I've been in the labs
the last couple of days.
I've read the blogosphere
the last couple of days
and I've gotten from, I've seen
from you guys some
amazing feedback.
I've seen the great things
you've started doing in Swift,
including the Flappy Birds
app in Swift, I've seen that.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We've gotten emails where
people tell us amazing things
about how they expect this new
tools to change their lives,
to make their programming
better.
Keep that coming.
Your feedback matters
a lot to us.
Let us know what
we're doing great,
let us know what
we can do better,
and of course, thank
you everyone.
I -- thank you.
[ Applause ]