Transcript
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Applause ]
>> Good afternoon.
Welcome.
Welcome to Intermediate Swift.
My name is Brian Lanier.
I'm an engineer in
Developer Publications.
Later I'll be joined on stage
by my colleague, Joe Groff,
who is an engineer on
the Swift compiler team.
We're both really excited to be
here today to talk with you more
in depth about the Swift
programming language.
I've been in the labs
the last couple of days
and I've been very
impressed already
with what you've been
able to do with Swift,
and I think that to go
into some more detail
on some really key features
of Swift, you'll be able
to take advantage of
those features even more
so in your code.
So, in particular, we're
going to look at some features
in some more detail
including optionals
and how you can use them
to make your code safer.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and how you can use them
to make your code safer.
We're going to talk to you
about memory management in Swift
and how it's largely automatic.
We're also going to talk
about initialization
and how you can take advantage
of the power of closures
and pattern matching
in your code.
So, I want to begin
by looking at one
of Swift's most powerful
features, optionals,
but first I want to take a look
at why we might need
optionals in the first place.
Say you need to work with
input data from a user.
Here we're asking
for the user's age.
The response comes in as
a string but say we need
to convert that string
to an integer using
this toInt method here.
Now when the enter
users a string
that represents a valid integer,
the toInt method returns the
correct result but, however,
because you're not in control
of the user's response you have
to deal with cases or situations
where there's not an
obvious value to return.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
where there's not an
obvious value to return.
The user has full control
over what they're entering in.
So, if they try to
get cute or snippy,
we don't really have a clear
value to return in this case.
So, what do we do?
What value do we return?
Well, we have lots
of options, right?
Well, as you're probably aware,
various sentinels have been used
in various different
languages to model these kinds
of problems, but
having so many sentinels
around isn't very good.
It's not very safe.
It's a common source of bugs
and problems in your code.
For one you have to know
which of these possible
sentinels was chosen
by the author of the API and
then you have to remember
which one to check against it.
So this isn't a very
good pattern.
So really how should
we model this?
Well, in Swift, we
model problems
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Well, in Swift, we
model problems
like these using
the optional type.
The optional type represents
possibly missing values
of any type whatsoever.
The optional type has 2 discrete
states; a default, nil state,
which represents literally the
absence of any value whatsoever
and optionals are
defaulted to nil.
You've probably seen a couple
of examples and options.
If you've seen the introduction
to Swift Talk, you write them
by writing the base height
followed by a question mark
to indicate their optionality.
Now, in Swift, nil is not
like Swift in Objective-C
where in Objective-C Swift is
an object pointer to nothing
and it only works for
the reference types.
In Swift, nil is a
true sentinel value.
It works with any
type whatsoever.
It literally just means
there's no value present.
The other state of an
option includes a presence
of a value that's been
wrapped up in the optional.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of a value that's been
wrapped up in the optional.
Think of optionals as this
wrapper container that wraps
up values when there
are values present.
So here we're setting the
value of optional number to 6
and the value is wrapped
up in the optional.
We'll see how to unwrap
this value and access it
in just a little while.
So, now that we have a single
sentinel value that works
with any type at
all even integers,
it's clear what this
method should return.
It's clear that whenever we
have a value that's not valid,
we should just return an
optional and, in fact,
the toInt method is defined
in the standard library
under string type and it does
just that it returns nil.
Now because we have optionals,
we also have non-optional
types as well.
So what are non-optional types?
Well, they're just ordinary
types you would think they are.
Integers, strings, even
your custom objects.
And the great thing about
non-optional types is
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And the great thing about
non-optional types is
that they can't be nil.
And we think this is pretty cool
because this makes your
code safe and predictable.
When you declare a value
of a non-optional
type, it can't be nil.
You can be sure that it's
there when you need it
and you can't be surprised
by unexpected nil values
propagating throughout
your code.
So, now that we know
what optional types are
and what they represent
let's see how we can use them
in our code, for example,
to write a function
that returns an optional type.
So here we're going to write a
function that looks for a string
in an array of strings and if
that string has found the array
it simply returns the index,
but as you might guess,
this is a case in which
if the string isn't found we
need some value to indicate
that failure and because
we have nil and optionals
at our disposal, let's go ahead
and change return pipe here
to an optional by adding the
question mark at the end.
So, let's implement
the function.
The first thing we need
to is enumerate the array
and here we're using
the enumerate method
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and here we're using
the enumerate method
to find the standard library
which actually enumerates
to an array and returns as
tuple, the index and the value
of each value in the array.
We can use that in the 4N
statement here and check to see
if the string that we're looking
for matches the value and if
so simply return the integer.
Now, we're returning an integer
here but then it gets wrapped
up as we said in the
optional return type
and because we have nil at our
disposal, we know exactly what
to do when we don't
have a valid result.
We just simply return nil.
Now, as I said, we need
to unwrap optionals.
So, let's use this function here
to look through an array and try
to find a name in this array.
Here you can clearly see that
the string that we're looking
for is in the array so we
have a valid result to return.
Then we can check though.
We always want to make
sure the value is present
so we can simply ask if the
index value isn't nil here
and notice that I don't, I'm not
checking explicitly against nil
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and notice that I don't, I'm not
checking explicitly against nil
and that's because optionals
can be used in Boolean context.
That means that you can check
them directly very naturally
like I've done here.
So if the value is present,
which it is, we can simply try
to use that value in the
subscript of the array,
but if we've done it like
this we have a problem
because the array is expecting
a non-optional integer,
but we've said that the function
returns an optional integer.
So the types don't match here
and the compiler
will let us know
that first before we use it we
need to unwrap the optionals.
One way we can unwrap optionals,
is using the force unwrapping
operator which simply consists
of an exclamation mark.
You write the exclamation
mark after the optional value
that you wish to unwrap.
And here because our search
returned a valid response,
we can simply print out
the result, but you have
to be careful as I
said we should check
to make sure the value is nil
first before we unwrap it.
So if we try to use the forced
unwrapping operator here
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So if we try to use the forced
unwrapping operator here
without checking, we would
actually get a runtime
assertion, a runtime
error because we need
to make sure the value
is there and we're trying
to force it when it's not.
So instead in Swift we have
a better way of doing this
where we can actually
test and unwrap
at the same time using what
we call optional binding.
Optional binding uses this
if let syntax you
may have seen earlier
in the Introduction
to Swift Talk.
So, the if let statement,
how does it work?
Well, first as I said
it tests so it tests
to see the optional type here
to see if it's actually present
or if its value is nil, and
if it's not nil, it assigns,
unwraps and assigns
a non-optional type
to the index value here,
this temporary constant,
and you can use a temporary
constant because it's unwrapped
as the index, as a
subscript to this array
and everything should
work just fine,
but we can actually
combine these together.
There's no need to actually
include a temporary variable.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
There's no need to actually
include a temporary variable.
We can simply evaluate the
result of the function directly
in line and then assign
that after it's unwrapped
to the index here, which now
index is a non-optional Int,
just regular old Int.
But that's what optional bind
looks like in a simple case.
Let's look at how it might look
if we did something a
little bit more complex.
So, to do that, let's set
up a couple of classes.
Say we have a person class
that has an optional residence
and a residence which
has an optional address.
Finally, we have
an address class
that has 3 optional
properties; the building number,
a street name and
apartment number all
of which are optional.
Let's do a little
bit more bookkeeping.
Let's go ahead and create
a person instance, Paul,
here and let's give
Paul a residence
and let's also give
Paul's residence an address
so he can receive his mail.
Now because Paul doesn't live
in an apartment we're not going
to set the apartment
number but we're going
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to set the apartment
number but we're going
to set the building
number to the string 243
and the street name
to Main Street.
Now let's say that we wanted
to get at the address number
as an integer not as a string.
Well we could use if let binding
to drill down through all
of these properties
and sub properties
of the classes we've defined.
So first we can test to see
if Paul has a residence.
If so, unwrap it,
assign it to home.
We can do the same with the
address and finally we can drill
down and use the toInt
method to convert that string
into a number and assign
it to the address variable,
but this is really
cumbersome as you can tell.
This does not lead to pretty
code and it's kind of hard
to follow all of the nested if
let syntax and statements here.
So in Swift we can actually
use optional chaining as a way
of accomplishing the same thing.
Optional chaining lets
you work with optionals
to conditionally
perform operations.
It's like messaging nil in
Objective-C but in such a way
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It's like messaging nil in
Objective-C but in such a way
that it works with
any type whatsoever.
Optional chaining provides
a clean and succinct syntax
for doing these kinds
of operations
so what we're doing here is
we're evaluating each point
using the question mark operator
or the chaining operator to see
if the thing to its left which
is an optional type if it's nil
or if it's present before we
proceed to the next expression.
So to see how this works
in a little bit more detail
let's look at this example
and see how it's evaluated.
You could think of
the evaluation
of an optional chaining
expression
like 2 parallel railroad
tracks that end
in two different
destinations or two stations.
The first station being the
optional value that's wrapped
up here so in this case an Int
that's wrapped in an optional
or simply nil when
there's no value present.
So let's see what this
looks like as we go
through the expression.
Well, Paul is non-optional
so that's obvious.
We've set him, he's
a valid instance.
We can go to the
first optional here
in the chain expression
residence.
Well, we know that we've
set it as a residence
but if it were nil, we would
actually just take the nil track
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
but if it were nil, we would
actually just take the nil track
and the entire expression
would return nil.
It's still an optional type
but it simply returns nil
and the rest of the expression
is ignored, but of course,
we did set a residence
and we can do the same
here along the path
and we can check every single
optional in a chained expression
to see whether or
not it's valid.
In this case, we've
set an address
and we've set a building
number and finally we come
to the last method call
after we've checked all
of the rest of the optionals.
Now the toInt method
as you recall returns
an optional type itself
so there's actually one more
path we need to consider.
It could, in fact, return nil
if it can't convert the value,
but we've set it, of course, to
a valid string representation
of an integer and so
toInt method has a result
to return 243 and the entire
expression is completely
evaluated it's wrapped
back up in an optional
so that the address
number is now an optional
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
so that the address
number is now an optional
with an underlying value of 243,
but because it's an optional
value if I need to use it
as a real integer, I
need to unwrap it still.
Actually we can combine
these operations together,
optional chaining, and if let
syntax are optional binding,
to test and unwrap it
at the same time again.
So here we can take the entire
optional chained expression
and evaluate it, see if it isn't
nil, unwrap it and assign it
to the address variable.
We can use it like an
ordinary non-optional type.
For example, to add
it to a database.
So that's how optionals work.
[ Applause ]
It's pretty cool.
So that's how optionals works.
It's how optional
chaining works,
how optional binding works,
I want to take a step back
and show you what optionals
look like under the hood.
I don't want to spend a lot
of time here but I do want
to show you how powerful
and expressive the Swift
programming language is
that we can actually model
something so fundamental
to the language like optionals
in the language itself.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to the language like optionals
in the language itself.
So optionals are really
just a simple enumeration,
a generic enumeration
at that, of any type.
You can see the sum type
here represents the case
in which I said there is a
value present that's wrapped up.
You can see that
wrapping here going on
and the None case just simply is
a default value which we've said
that you can indicate using nil.
We haven't talked too much about
generics and we're not going
to go into generics in much
detail here, but I encourage you
to watch the Advanced Swift talk
where you can see the full power
of generics in action.
So that's optionals.
You use optionals to
work safely in your code
with possibly missing values.
Missing values are nil
and present values are
wrapped up in the optionals.
You can then unwrap the
optionals in two ways;
either with a forced-unwrapping
operator but only do
that if you're sure there's a
value there and it's not nil,
and you can also use if let
syntax or optional binding
to unwrap and test at the
same time in a very safe way.
And finally, you can use
optional chaining to work
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And finally, you can use
optional chaining to work
in a very succinct
and eloquent way
with multiple optional
expressions chained together
and that's optionals.
So, I'd like to invite Joe back
on the stage to talk to you
about memory management
in Swift.
[ Applause ]
>> My name is Joe Groff, I
work on the Swift compiler,
and I'm really excited because
managing memory is my favorite
thing to do when I program.
No, it's not.
Thankfully Swift is built
on automatic reference
counting the same model we used
in Objective-C or ARC.
We don't make you use pointer
syntax, we don't make you spell
out Alec, but still we need
a clear class like this
and a constructed class you get
a memory allocation implicitly
and a reference to that is what
gets stored in a local variable
and it's these references
that keep that memory alive.
Now we can juggle a couple
of these bowling pins here
and when the last reference
to a class goes away,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and when the last reference
to a class goes away,
an object goes away, the object
automatically gets deallocated.
ARC is a horrible
juggler but it's great
at reclaiming unused memory.
When a reference
goes out of scope,
it's automatically
released but as long
as you have some reference to
the object, it's kept alive
and only when the final
reference is released is the
object deallocated.
It's safe, it's predictable,
it just works.
Except for those cycles.
Sometimes it doesn't make sense
for an object to be owned.
For instance, apartments
literally aren't owned
by their tenants but
they also will have
like multiple people
living in them
over the lifetime
of the building.
On the other hand, a person
will move from apartment
to apartment over their life.
Even if you move a
person into an apartment,
this relationship isn't
really an owning relationship.
And there's a problem if
we try to model it as such.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And there's a problem if
we try to model it as such.
Let's set up a dictionary
of our renters
and our apartments
say we're a landlord.
Let's move one of tenants,
Elsvette, into her apartment.
Now there's a problem.
If she tries to move
out, she's trapped.
The reference to the apartment
keeps her object alive
and even worse if we try to sell
off the property we're stuck
with the deed.
Both objects keep each other
alive in a reference cycle.
So like Objective-C,
we have weak references
and these tell Swift that
this object isn't responsible
for keeping the object
on the other end alive.
Now, when we work through the
example again, a weak reference
between these two
objects Elsvette can move
out of her apartment and make a
clean break with her landlord.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And the reference
automatically gets reset to nil.
There's no dangling reference
to the allocated object.
Now of course, when we sell off
the property, both objects end
up deallocated and
there's no leak.
And weak references in Swift
are modeled as optional values.
You can use all the
optional operations
that Brian just showed
you to work with them
and when you take an
optional weak value
and you bind the
non-optional part
out of it you get a
strong non-optional value
and you can safely
work with that just
like any other reference.
If you're applying a
single method or looking
at the single property
conditionally
on a weak reference object,
you can also use the chaining
operator as a nice shorthand.
Some things to be aware
of are that the test
and unwrap pattern does not
work very well with weak.
You could go to cash your
tenant's rent check and find
out it bounced and evict
them before you have a chance
to even greet them and end
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to even greet them and end
up with a runtime error
you didn't expect.
Chaining also doesn't preserve
a strong reference if you use it
to try to apply multiple
methods to the same object.
Here cashRentCheck could again
cause the tenant to get released
and then we'd end up getting a
nil object and then, in fact,
not calling greet at all.
Weak references are
great for breaking cycles
but they aren't always ideal.
Let's say we're a
modern landlord
and we accept credit card
payments from our tenants.
A dream I know but bear with me.
Now a credit card
should only ever be owned
by one person, right?
So we really want this to
be an immutable binding,
an immutable property
of the credit card.
We also don't even really
want to track a credit card
if it isn't owned by a person.
It shouldn't be an
optional property.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
There's obviously a cycle here
and we could break that cycle
with weak references
but that forces us
into some unfortunate
loosening of the model here.
First of all weak
references have to be optional
because they have to
be resettable to nil.
Second of all they
have to be mutable
because they could be
reset to nil at any time
and we don't really want this.
We could end up accidently
reassigning a credit card
or ending up with an
orphan credit card just
to deal with memory leaks.
That would suck.
So, instead, we have
another kind
of weak reference the
unowned reference.
What this tells Swift is
that although I don't have an
owning stake in the object,
my life depends on it.
I cannot live without my
owner, my card holder.
So we can go back to
our renter's table,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So we can go back to
our renter's table,
we can save our tenant's
credit card information
and you can see here that the
credit card is uniquely owned
by the person record so that
when her record is reallocated
the entire object graph
gets allocated.
There's no need to observe
the credit card independent
of the person.
And because there's
this assumption
that the program is not
correct without the object
on the other side, you can
use unknown references just
like strong references.
You can assign them
into a local variable
or you can call methods
on them directly.
It's a bit like unsafe,
unretained in C
or Objective-C but
it's still safe.
We still assert that the object
is still allocated every time
you access it through
an unowned reference.
So let's look at three kinds
of references in Swift.
Strong references are
the default and that's
because they're what you
should use most of the time.
Most of the time you
have an ownership stake
in an object you want to use.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in an object you want to use.
However, if you have
independent objects,
objects with independent
lifetimes, sort of a, you know,
a casual relationship
going on where you can move
on if they went away,
weak references are great
for modeling that sort of thing.
If part of your program
ends up deallocated due
to maybe low memory, the rest
of your program marches on
and optionals are a
great way of dealing
with weak referenced
objects going away.
And unowned references are
great for back references
from dependent objects
backed up to their owners.
If part of the object
graph goes away,
the entire object
graph goes away
and there's no need
to deal with nil.
And that's memory
management with Swift.
It's automatic, it's automatic
reference counting, it's safe
and it lets you think
about the relationships
between your objects rather
than the raw mechanics
of memory management
making strong,
weak and unknown references
to model those relationships
and provides a nice, safe
foundation for Swift.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and provides a nice, safe
foundation for Swift.
To talk a little bit more
about that foundation I'm going
to bring it back to Brian to
talk about initialization.
[ Applause ]
>> Thank you, Joe,
thank you, Joe.
So I want to talk to you
about initialization in Swift.
Initialization in Swift follows
one very simple rule and that's
that every value must be
initialized before it's used.
This one simple rule is
enforced by the compiler
and it ensures memory
safety in your code.
It ensures that you never
read from uninitialized memory
and it, therefore,
helps eliminate an
entire class of bugs.
Let's look at how this rule
applies throughout the language.
So let's start with
the simple variables.
Swift doesn't default
initialized variables,
constants, or properties because
there often isn't a natural
and correct value
for everything.
The only exception to this is
optionals where we saw earlier
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The only exception to this is
optionals where we saw earlier
that optionals have a very
natural default value of nil.
This means that you need to
set these values explicitly
in your code, but it
doesn't mean that you need
to declare these
values at the point
that you declare your variable
as constants or properties.
It just means that you must
do so before you try to read
from them or access them.
For example, say we
wanted to set message here
to an appropriate value
depending on whether
or not a condition was true and
then print that message out.
Even though the condition is
true in this case, I mean we're
in the middle of the
session after all,
we'd get a compiler error
because we haven't considered
every possible branch
in our code.
We haven't said what
the value of messages
when the condition is false.
So let's add an else
clause to do just that.
Now when we read for message
to print it out we can be sure
that it has an explicit value
for every single condition
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that it has an explicit value
for every single condition
in our code, but now as I'm sure
you're all aware it's quite easy
to forget to check all of
the conditions in your code.
So rather than surprising you
by unexpected and hard to track
down behavior, the Swift
compiler catches these kinds
of mistakes for you and
ensures that you're explicit
about the values you want
to set for every branch
and under every condition
in your code.
This is great for memory safety.
Now that we've seen
how this rule applies
to simple variables,
let's see how it applies
when you're defining your
own classes like, classes
or structures, your own types.
So, inside of the structure are
class declaration initializers
handle this responsibility.
They handle the responsibility
of satisfying the rule
that everything is
set before it's used.
Now, you've seen some
initializers in some
of Joe's examples and memory
management just a while ago
and they're declared
using the key word in it
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and they're declared
using the key word in it
and they're invoked
at object creation
or instance creation here
using this initializer syntax.
Now initializers can have any
number of labeled arguments
or parameters and when
they do you need to call,
you need to call it using
these label parameters.
They're required
at the call site.
Before we look at initialization
in classes, let's take a look
at initialization and how
it works in structures.
Let's define a simple
color structure
that has three properties,
red, green and blue
to represent the color
components and we'll deal
with alpha later, now let's say
that we want to create colors
in different shades of gray.
Well to do so we can define a
single initializer here a very
simple one that simply has,
takes a single gray scale
double value and sets all
of the stored properties of the
structure to that same value.
Now, if we had forgotten to set
the red property, for example,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, if we had forgotten to set
the red property, for example,
we'd get a compiler error
because we haven't
satisfied the rule
that every value must be
initialized before it's used.
The same is true
for calling a method
in your initializer before you
set all of your properties.
For example, say we want to
validate, we want a method
that validates the color
components to make sure they're
within a valid range
of values say 0 to 1.0.
If we then call this
method here inside
of the initializer before we
set all of our properties,
we'd again get a compiler
error because we're trying
to call a method on
self on the instance,
but before self has
been fully initialized.
So if you need to access self
just customize any property
values or to call on the
methods in your initializer,
always do so after you've set
all of your stored properties
to appropriate values.
Now in structures, if
you don't define any
of your own initializers,
Swift provides you
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of your own initializers,
Swift provides you
with a memberwise
initializer automatically.
The memberwise initializer
has arguments that correspond
to each of that instances
store, each of that classes
or structures stored property.
You can also provide default
values in line directly
for your stored properties
and when you've set all
of your stored properties
and you haven't defined any
of your own custom initializers,
Swift provides a default
initializer for you to use
that takes no parameters
and creates an instance
with the properties you
set as default values
when you declare your structure.
So that's how initialization
works in structures.
Now let's take a look at
how it works in classes.
Really it's no different.
So let's start with a
simple car class here
that has a single
property, a paint color,
and a single initializer that
just sets that paint color
to an appropriate value.
Now, in practice, classes
and often subclass
inherit from other classes.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and often subclass
inherit from other classes.
So we need to deal with
how initialization works
with subclasses as well.
So let's define a
subclass called RaceCar
and it has one extra property
and its own initializer.
When the RaceCar's
initializer is called,
it first sets its own properties
and then it called the
super class initializer.
And the super class
initializer, of course,
sets its own properties to
an appropriate value as well
and only after the super class
and all the super classes
in the class hierarchy
have had a chance
to set their own values is
a class fully initialized
and ready to use.
Now if you're coming from
an Objective-C background,
you probably noticed that
this is opposite or different
than what you've noticed than
what you do in Objective-C
where you always call
your super class first
and then you set
your own properties.
So in Swift if we
were to do this,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So in Swift if we
were to do this,
if we were to call our super
class initializer first
and then set our own properties,
we would actually
get a compiler error
because it's not always
safe to do so in this order
and the reason Swift's
initialization follows this
order is that it ensures memory
safety so that you don't try
to access memory before it's
been completely initialized.
Now it may not be obvious where
you might run into problems
by doing things in this order so
let's take a look at an example
where calling your super class
initializer first is unsafe.
Let's say, for example, that
we want to define a function
or method on our car class
that fills up the gas tank
and we want to call that
function inside the initializer
so that every time we
get a new car it comes
with a full gas tank and because
race cars use a different kind
of gas than regular cars,
we've overridden this
method in our subclass.
So, when we create
a new race car
and its initializer is called,
it's first called
super class initializer
which then sets its
paint color properties
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
which then sets its
paint color properties
and finally calls its
fillGasTank method,
but because we've overridden
our subclass it gets dynamically
dispatched to the subclass's
version of that same method
and that's where
we have a problem.
And our problem is that
we haven't got a chance
in our own initializer
to set our own properties
and so we're trying to
call a method on self.
We're trying to access this
object or this instance
that hasn't been fully
initialized and that's not safe.
So in Swift, we always make sure
we set our own properties before
we call our super
class's initializer
to have a chance to do the same.
So far we've just seen
examples of a single initializer
in a class, but you can
have multiple initializers
in your class just like
you can in Objective-C.
In fact, you can have designated
initializers what we've seen
so far designated initializers
which you may be familiar
with coming from Objective-C
have the sole responsibility
of creating an object and
setting all of its properties
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of creating an object and
setting all of its properties
and it has a job of calling
the super class's initializer
and delegating up.
Like you can think of
them as funnel points
that funnel the initialization
process all the way to the class
to the base class and hierarchy,
but you can also have
convenience initializers.
Convenience initializers are
sort of secondary initializers
that provide an alternative
implementation initialization
interface that allows you
to say pass default values
and make it easier for
your clients to use.
They can only call across.
They can only delegate side
to side in a particular class.
They never call up.
So they can call designated
initializers in the same class
or other convenience
initializers
but ultimately these
convenience initializers need
to call a designated to
actually do the heavy lifting
of setting the properties.
So let's see how you might
write a convenience initializer
in your own code.
Say we have RaceCar
again and we want
to create a convenience
initializer
that lets it just
pass in a color
and it sets the turbo
property to true and it does
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and it sets the turbo
property to true and it does
so by delegating this task
to the designated
initializer in the same class.
Now when we do this
kind of thing in Swift,
when we define a
convenience initializer,
we need to let the
Swift compiler know
by including the
convenience key word
in front of the declaration.
We can even provide more than
one convenience initializer
so we can even provide a
convenience initializer
that lets us create a RaceCar
providing no parameters at all
and then delegates this task
here providing a default gray
value and it calls the other
convenience initializer we've
just declared and that
in turn, of course,
has to call the designated
initializer
to actually set the values and
because we have a super class
to deal with here,
that initializer needs
to call its super class.
And how are initializers
inherited in Swift?
Well, they're not
inherited by default
because doing so
isn't always safe.
However, when your class
provides no initializers at all
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
However, when your class
provides no initializers at all
and you set default values for
all of its stored properties
as we've done here with
the FormulaOne RaceCar
where we've set a
single stored property
to a minimum regulation
weight and since we know
that weight we can
set it directly.
Now when we do this, we actually
inherit automatically all
of the initializers
of the super class
that includes designated
initializers
and the convenience initializers
that we've declared.
Now, if we did this in
this particular case,
we'd have a problem because one
of the convenience initializers
we've inherited sets the wrong
value for turbo.
Formula One race cars aren't
allowed to have a turbo
by regulation and so this
wouldn't be appropriate
in this case.
So instead we'll define
our own initializer here
and now it's a designated
initializer
because it calls super and
passes that turbo value of false
and because we've done
this we've provided our own
initializer, we actually don't
inherit those other convenience
initializers or the other
designated initializer.
So that's how you would use
initializers to set properties
and how you can set
properties in line directly,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and how you can set
properties in line directly,
but I haven't told you about a
pattern and you may be familiar
with from Objective-C sometimes
called lazy instantiation
or lazy initialization where
you want to delay the evaluation
of a property in setting
it because, for example,
maybe doing so is
computationally expensive.
So we have the same concept
in Swift using lazy properties
and let's take a
look at an example
of where you might use one,
where it might be appropriate.
Say we have a game class and
the game can either be a single
player game or a
multi-player game.
Now, the multiplayerManager
let's just suppose
that it does a bunch of
expensive computation
and so we don't want
to create it
if there's only ever
a single player.
We only want to create it
when there's multiple players.
So we want to delay
that initialization
until it's appropriate.
So we can do this in Swift by
including the @lazy attribute
in front of the declaration here
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in front of the declaration here
and when we do this it will
only be evaluated the time it's
accessed and only when it's
accessed and only that one time.
Now just as we have
initialization
in Swift, we also have --
[ Applause ]
Just as we have initialization
in Swift we have
deinitialization.
Deinitialization is responsible
for tearing down the object.
Now as Joe has already
told you, memory management
in Swift is automatic so most
of the time you don't even need
to think about the
initialization it just works,
it just happens for you.
But for cases where you
do need it, for example,
to unregister yourself as
an observer or, for example,
to close a file that
you may have opened,
we want to have a chance to
close that file and clean
up those non-memory resources.
So for that we use
a deinitializer
and deinitializers are just
declared using the deinit
key word.
So, for example, here we have
a class that when we open
up a file if this class gets
deallocated before we have a
chance to close the file,
that's not good so think of this
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
chance to close the file,
that's not good so think of this
as the last resort, the last
chance you have to clean
up non-memory resources before
your object is deallocated.
Again most of the time you
probably won't need them.
So that's initialization
in Swift.
It's safe.
You initialize all of your
values before you use them
and set your stored
property values first
and then call your super
class initializer always
in that order.
Designated initializers
only delegate up
and convenience initializers
only delegate across.
And finally, we have the
initializers if you need them,
but most of the time you won't.
So that's initialization.
I'd like to invite Joe back
on the stage to talk to you
about closures in Swift.
[ Applause ]
>> Now, there are a lot
of ways to sort an array
and we could keep our standard
library writers busy writing a
million different sort methods
on array, but we'd rather not.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
million different sort methods
on array, but we'd rather not.
So instead we have a single sort
method that takes a closure.
You've probably seen a bit
of this in the intro talk
or even in the keynotes.
A closure in Swift is
spelled inside curly braces.
You follow it with a signature.
In the case of sort,
it takes two arguments
of the element type of the
array, in this case String,
and in terms of Boolean telling
us whether the elements are
sorted in the right order.
Default with the in key word,
you're binding these arguments
inside the body of the closure
and then you follow it with
the body of the function.
And now we have a single sort
method that can sort an array
in a number of different ways.
We can sort A to Z ascending
using the lesson operator.
We can sort descending using
the greater than operator.
We can even do something like
sort from shortest to longest
by counting the number of
characters in each string.
Now this is a great interface.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now this is a great interface.
It's also a lot of
typing and closures
in this form are a little
awkward to work with
and the actual interesting
part gets lost
in a lot of syntactic noise.
Thankfully we have type
inference to help us here.
Let's open a definition
of sorts.
Sort, of course, already knows
what kind of closure it takes.
In this case, we're working
with an array of strings
so it takes two strings
and returns a Bool.
Let's look at the
call side again
and that's really offensive,
that's really burning
my eyes here and I want
to do something about that.
Much better.
We can infer the
argument and return types
from the signature
of the function.
That's a great improvement
but we don't stop there.
If a closure consists of
a single return statement
like this, we don't
need to specify return.
A single expression closure
implicitly returns its results.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
A single expression closure
implicitly returns its results.
[ Applause ]
Thank you.
At this point even the
argument list is starting
to look a little heavy.
Let's get rid of
that too [laughter].
There are implicit argument
names, $0, $1, $ a million,
they may need a little taste
in some cases but they're there
if you need them and they
make closures really succinct
and easy to use.
We've also seen that we have
trailing closure syntax.
When you have a single
argument like this,
you can move it outside and when
it's the only argument you can
drop the parens all together.
And this makes functional
programming
in Swift really expressive
and fun to do
and we've added methods to the
array class, to the array struct
to make this possible.
I'll start with a
list of all the words
in the English language that we
got from the dictionary service.
Now we can pick out words
that share some common traits,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now we can pick out words
that share some common traits,
say the ones that end
in G-R-Y and it turns
out there's only two and we
can use the filter method
which selects out, which applies
a closure to each element
of the array and for the one it
returns true returns a new array
containing those.
We can then transform
these with the map method,
which applies closure
to each element
and collects the results
into another array.
And then we can use
the reduced method
to fold these into
a single string.
Now each of these is
chaining another method call
onto the result of the
previous expression.
Even if we're using trailing
closure syntax we can just chain
method invocations
using dot like this.
Because it's a single expression
we can even include it in a
yet larger expression and
include a lot of complex logic
into a very small
amount of code.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
into a very small
amount of code.
It's an extremely expressive
way to build up your logic even
for a code that doesn't
involve Hulk.
[ Applause ]
Closures can also
capture local states.
We could sum up all the elements
in array using a for loop
or reduce but we can
actually abuse math to do it.
Inside of closure we can
refer to local variables
and we can even mutate them.
There's never need to mark
them under block or in any way.
It just works.
And then we can return
that result.
Closures are really just
literal functions in Swift just
like you can pass
a literal integer
or a named integer constant
as an integer parameter,
you can pass a closure
or, indeed,
a named function
as a closure value.
We don't have to wrap up print
line in a closure like this.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We don't have to wrap up print
line in a closure like this.
We can just pass print line.
There's only one kind of
function type in Swift.
We can even do this with
bound methods instead
of packing an index set
like this wrapping a call
to the method in a closure we
can just pass the bound method
directly as a function value.
Closures in Swift
are just ARC objects.
They follow the same memory
management rules as classes.
So if we wanted to
have a global variable
that we can install a call
back on and write a function
that installs a call back using
local state of that function,
that reference will keep
that closure and all
of its local state alive.
There's never a need to
explicitly copy a closure
or to worry about dangling
references to its environment
and just like closures
of functions,
functions are also closures.
We can nest functions inside
other functions and refer
to the local state of
the outer function.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to the local state of
the outer function.
If we look at these in the
debugger or instruments,
they show up with their names.
So when we store a reference
to this in a global variable,
even when the local
function goes out of scope
that reference keeps the
local function alive just
like the closure.
Now because closures are ARC
objects they have the same
ownership and reference cycle
problems as classes can have.
If we did something
as seemingly innocent
as taking this call back and
putting it into the property
of a class, we'll
have a problem.
When we go to set up
these objects, of course,
what we're really doing
is we're capturing self
and we're capturing it as
a strong reference and then
when we store reference to
the closure inside the class,
we end up with the reference
cycle and a memory leak.
The compiler actually won't let
you implicitly reference self
inside of closure
because of this problem.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
inside of closure
because of this problem.
Yeah, it's pretty nice.
Now in a past life, you may
have solved this using unknown
reference, using a local
variable with weak ownership.
In Swift, we can use
unowned ownership instead,
but this is problematic.
Even if we did this if
someone cut and pasted code
into this closure, it
would still be capturing
self strongly.
So we have a better
way to do it in Swift.
You can specify directly
inside the closure how
to capture its local states.
[ Applause ]
And now when we set
up the object graph,
there's no longer a cycle
and there's no leak.
So that's closures in Swift.
They have an incredibly
expressive syntax
and a much simplified memory
model from Objective-C
and they make functional
programming
in Swift really awesome
and powerful.
Now I'd like to talk about
another powerful feature
of Swift, pattern matching.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of Swift, pattern matching.
You may have seen in the intro
talk that Switch not only works
with integers it also works
with strings or, indeed,
values of any type and also
works with ranges of values
but that's just the
tip of the iceberg.
You may also remember
that enumerations
in Swift can carry associated
data and we can tell not only
that a train is delayed
we can say by how much
with an integer value
that only makes sense
when the train is delayed.
Because this associated data is
tied to that case of the enum,
we access it through
a case statement.
We combine it to a
variable and then use
that variable inside
that case statement.
It's electrically
scoped to that case.
Cases are electrical
scopes by default in Swift
and we can't access
it anywhere else.
It's much safer and easier
than using an enum with a union
or struct like you
may have in C.
Now this is a simple
case of a pattern.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now this is a simple
case of a pattern.
This outer construct
Delayed only matches values
of the delayed state
inside the enum.
If it is at the delayed state,
it unwraps the associated data
and passes it down
to a sub pattern.
In this case, the let pattern
binds it to a variable.
This is actually a
completely independent
and fully powerful pattern.
Anything we do at the
top level of a switch
with an int we can do
to its associated value.
So we can match it against a
specific value say we're delayed
by 1 minute.
We can match it against a range
of values say we're delayed
from 2 to 10 minutes.
We can also ignore the
value all together,
match any Delay using
the underscore,
which is the wild card pattern.
[ Applause ]
You can also use enums as
associated data of other enums.
We can track not only the status
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We can track not only the status
of our train while we're
traveling we can track the
entire state of our
vacation and when we do so,
we can tell our friends
on social media based
on that state.
We can match a simple
case of the enum,
we can match a nested associated
value two levels deep.
If we're 15 minutes in, we might
be able to, 15 minutes delayed,
we might be a little
snarky, but still composed.
If we're delayed
any more than that,
we might get a little upset.
Pattern matching
doesn't just work
with enumerations it also works
with the dynamic
types of classes.
Let's say we're going to tune up
a car and it's an arbitrary car.
If someone brings a Formula
One car into our mechanics,
to our mechanic, he's probably
going to be a little confused.
He might want to take
it to a specialist.
We can do this using
the as pattern.
This will both check that
the value is of a type and if
so cast it and pass it
into the sub pattern,
which we can then use to bind a
variable and then we can pass it
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
which we can then use to bind a
variable and then we can pass it
on to a pit crew who will then
tune up that Formula One car.
For a more mundane car, we
can pattern match it and then
if it has a turbo tune up
the turbo before following
through using the fall through
statement into the default case
that does the normal
tune up for a car.
We can also pattern match
multiple values simultaneously
using tuples.
Tuples are a great way of
returning multiple values
from a function or for
combining related values
like the components of a color
and when we do so, each element
of this tuple pattern is
an independent pattern.
We can match red against a
single value, we can match green
against a range of values,
bind blue to a variable
and ignore the alpha component
all in a single pattern.
[ Applause ]
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We can also match
multiple values
and test additional conditions
with those values
using a where clause.
Here we're testing that
they're all equal to see
if it's a grey scale value.
So these are some
neat parlor tricks.
Let's try something
a little more real.
Let's validate a property list,
a dictionary of arbitrary values
of unknown type and
given a valid one we want
to return a well-typed struct.
Given a property list with
a name key that's a string,
a population that's a number
and a two-letter
postal abbreviation,
we want to get a
record like this.
However, if one of the
fields is of the wrong type
or if the postal abbreviation is
too long, we want to return nil.
Let's see how we can do
this with pattern matching.
We'll start with a single
key of the property list
and you may remember that
optional is just an enumeration
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and you may remember that
optional is just an enumeration
so we can use an
enumeration pattern
to both reject property
lists that don't have the key
and unwrap the value
for ones that do.
Inside that pattern we
can use a type pattern
to both reject property lists
that don't have a string name
and get the string value
out for those that do.
Then we can use a let pattern
to bind to that string value
and store it to a variable
with a single default clause
handle all invalid names.
Now we can repeat this for
each element that we care
about inside the dictionary
but there's a better way.
Remember we can use
tuple patterns
to match these simultaneously
and repeat this pattern
for each key of the property
list that we're interested in.
We can see that the
name is a string,
the population is a number
and the abbreviation is a string
all simultaneously and with the
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and the abbreviation is a string
all simultaneously and with the
where clause even
check the abbreviations
of the appropriate
length and we're done.
With a single default clause
we handle all invalid property
lists and it all fits
nicely on a single slide.
That's the power of
pattern matching.
[ Applause ]
It's an incredible way to
test the structure of values
and really improve
the readability
and safety of your code.
So we still just scratched the
surface of what Swift can do.
We've looked at how optionals
allow you to write safe code,
we looked at the
foundations of Swift
in memory management
and initialization.
We looked at how
powerful closure is
and pattern matching are.
There's a ton more
information online.
There's the book that you
probably already downloaded
and read by now.
There's also some additional
material on the developer's site
about Interop with Cocoa.
If you're following along from
home, talk to Dave he's great
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If you're following along from
home, talk to Dave he's great
and there's our forums.
You also might want to
save your seat here.
We've got a lot of other great
Swift sessions right here
in Presidio.
Thank you.