Swift supports rich first-class value types in the form of powerful structs, which provide new ways to architect your apps. Learn about the differences between reference and value types, how value types help you elegantly solve common problems around mutability and thread safety, and discover how Swift's unique capabilities might change the way you think about abstraction.
I'm here with my colleague Bill Dudney, and we will talk
about building better apps with value types in Swift.
First, we are going to start off talking
about reference semantics, and then we will delve
through immutability as a solution to some of the problems
that reference semantics pose.
Dive into value semantics and value types, how it works,
especially how it works in Swift, and then talk
about using value types in practice and then the mixing
of reference types and value types together within Swift.
Let's get started.
So the way into reference semantics
in the Swift world is to define a class.
Here's a very simple temperature class.
We are storing our temperature value in Celsius.
We want to have this nice computed Fahrenheit property
so we can always get to it with the correct unit.
Simple, abstracted version of a temperature.
Let's try to use it in some simple code.
We create a home instance, we create a temperature instance.
We set our thermostat to a balmy 75 degrees Fahrenheit.
Now we decide, oh, it's getting close to dinnertime
and I want to bake some salmon.
So I set my oven to 425 degrees and hit Bake.
Why is it so hot in here?
What's going on?
You know what happened.
We hit this case of unintended sharing.
Think of the object graph.
We have our house.
It has a thermostat and an oven.
We have this temperature object that temp points to.
When we set our thermostat,
we wired it to the same object as the temp.
Things look like fine until we go ahead and do a mutation,
and now this unintended
or unexpected sharing just caused us to set our thermostat
to 425 degrees Fahrenheit.
At this point it was already game over,
but just for good measure let's wire our thermostat to our oven
because we've lost already.
So what did we do wrong?
In this world where you have reference semantics,
you want to prevent sharing, and so you do copies.
Okay? Let's do this the right way.
Okay. I'm going to set my temperature
to 75 degrees Fahrenheit again.
When I set the temperature of my thermostat, I will cause a copy.
So I get a brand new object.
That's what my thermostat's temperature points to.
When I go ahead and change the temperature
of my temp variable, it doesn't affect it.
Now I go and set the oven's temperature,
I will make another copy.
Now, technically, I don't need this last copy.
It's inefficient to go waste time allocating memory
on the heap to create this extra copy.
But I'm going to be safe because the last time I missed a copy,
I got burned [groaning]!
Come on, it's a Friday session, give me a break!
So we refer to this as defensive copying, where we copy not
because we know we need it, but because if we do happen
to need it now or sometime down the road, it's really hard
to debug these problems.
And so it's way too easy
to forget a dot copy any time we assigned a temperature somewhere
in our oven.
So instead, I will bake this behavior right
into my oven [laughter].
All right, I'm done.
I'm done. I'm sorry.
I'm sorry [applause].
Naturally I have to do this exact same thing
for the thermostat, all right?
So now we've got a whole bunch of boilerplate we are copying
and pasting, and sooner or later, you will be knocking
on my door asking for a language feature here.
Hold off on that, and let's talk about where we see copying
in Cocoa and Objective-C.
So in Cocoa, you have this notion
of the NSCopying protocol.
And this codifies what it means to copy,
and you have a whole lot of data types and this NSstring,
NSArray, NSURLRequest, etcetera.
These things conform to NSCopying because you have
to copy them to be safe.
We are in a system that needs copying, and so you see a lot
of defensive copying for very, very good reasons.
So NSDictionary will defensively copy the keys you place
in the dictionary.
Why? Well, if you were to hand NSDictionary a key to insert it
and then go change it in a way that, say, alters the hash value
of it, you will break all the internal variance
of NSDictionary and blame us for your bugs.
We don't really want that.
What we really do is we do defensive copying
That's the right answer in this system, but it's unfortunate
that we are losing performance
because we are doing these extra copies.
Of course, we moved this all the way down into the level
of the language with copy properties in Objective-C
that do defensive copying on every single assignment to try
to prevent these problems, and it helps,
all this defensive copying helps,
but it's never good enough.
You still have a ton of bugs.
So we have problems with those reference semantics
and there's mutation.
Maybe the problem here is not the reference semantics
but the mutation itself.
Perhaps we should move to a world
of immutable data structures with reference semantics.
If you go talk to someone
in the functional programming community, they will say, yeah,
we have been doing this for decades!
And it does improve things there.
So you can't have unintended side effects in a world
where there are no side effects,
and so immutable reference semantics are a consistent way
to work in the system.
It doesn't have these unintended consequences, which we ran
into in our little temperature example.
The problem is that immutability has some disadvantages.
It can lead to some awkward interfaces,
and part of this is just the way the languages work,
and part of this is that we're used to living in a world
where we can mutate things, and we think about state
and mutating state, and so thinking
in purely a immutable world can be a little bit weird
for us at times.
There's also the question of an efficient mapping
down to the machine model.
Eventually, you have to get down to machine code,
and you are running on a CPU that has stateful registers,
and stateful caches,
and stateful memory, and stateful storage.
It's not always easy to map an immutable algorithm down to
that level efficiently.
Let's talk through a couple of these.
We will take this temperature class of ours and try
to make it safer by making it immutable.
So, all we had to do here was change the stored property
Celsius from a var to a let, and now you can't change it.
And then the Fahrenheit computer property loses its setter.
So you can't change the state
of temperature no matter what you do.
We add some initializers for convenience.
Okay. Let's talk about awkwarding.
Awkward immutable interfaces.
Before, if I wanted to, say, tweak the temperature of my oven
by 10 degrees Fahrenheit, this was a simple operation.
Plus equals 10.
That does it.
How would we do this if we have taken away mutation?
Well, we have to go and grab the temperature object in the oven,
create yet another temperature object that has the new value.
So it's a little bit more awkward.
There's more code, and we wasted time allocating another object
on the heap.
But in truth, we have not embraced immutability here
because we did an assignment that mutated the oven itself.
If we were embracing the notion
of immutable reference types throughout here,
we would be creating a new temperature to put in a new oven
to put in a new home [laughter].
Awkward! So let's get a little bit more theoretical
and do some math.
So the Sieve of Eratosthenes is an ancient algorithm
for computing prime numbers.
It uses mutation and actually lends itself well
to drawing things out in the dirt with a stick.
So this is the implementation of the mutating version in Swift.
We are going to walk through it so you get the idea behind it.
First thing you do: create an array.
Notice the var because we are going to mutate this array.
Notice this array is from two, first prime number,
up through whatever number you want to compute.
We will do 20 here.
Now, the outer loop, each time through it,
we will pick the next number in the array.
That number is a prime number, P.
What the inner loop is going to do is walk over all
of the multiples of P, erasing them from the array
by setting them to zero.
Because, of course, if you are a multiple
of a prime number, you are not prime.
Back to the outer loop,
we go and grab the next number, it's a prime number.
We erase all of its multiples from the array.
So it's a very, very simple algorithm.
Think of the stick in the dirt.
You are just scratching things out.
Once we are done going through all
of our iterations, we go down here.
And we do the last simple operation, which says,
everything that we have not zeroed out in the array,
that's part of our result.
So we will do that with a filter.
Simple algorithm, entirely based on mutation.
Now that doesn't mean you can't express it
in a world without mutation.
You can. Of course.
So to do that, we are going to use Haskell,
because it's a pure functional language [applause].
Yes, I knew people would love it!
So this is the Haskell formulation.
It's -- if you read Haskell, it's beautiful.
It doesn't mutate at all.
Here's a very similar implementation because it turns
out Swift can do functional also, and if you want
to make it lazy, that's an exercise to the reader
but it's not that much harder.
And we're going to walk through how this algorithm works,
because It's very, very similar.
We start with our array of numbers from 2 to 20.
In the simple basis case, if there's no numbers, well,
there's no prime numbers in there.
So that's the first If statement.
Otherwise, what you do is we take out the first number,
that's always going to be prime.
And separate it from the remaining numbers.
Right? Haskell did this with pattern matching,
and we can do slicing here.
Then we take that prime number and we run a filter operation
over all of the elements here in this remaining array.
Copying only those things
that aren't multiples of that prime number.
Now we recurse and do it again.
Split out the three and this is our new prime number.
Go ahead and run the filter.
Eliminate all the multiples of three, and so on and so forth.
What happens here is you end
up building along this left-hand diagonal the actual prime
numbers, and as a result they all get concatenated together.
The idea is similar.
It's very, very similar.
But it's not the same algorithm
because it has different performance characteristics.
So this result comes from a brilliant paper
by Melissa O'Neil called "The Genuine Sieve of Eratosthenes,"
where she showed the Haskell community
that their beloved sieve was not the real sieve
because it did not perform the same as the real sieve.
She goes through much more complicated implementations
in Haskell, that can get back to the performance characteristics.
Read the paper and check it out.
It's really cool.
I want to give you a taste of why this is the case.
Look at either the Haskell list comprehension
or the equivalent Swift filter below.
In this nonmutating version, this operation will walk
over every single element in the array
and do a division operation to see if it should still be
in the next step, to see if it's a multiple of P or not.
In the original mutating algorithm, we only walked
over the multiples of the prime number and those, of course,
become increasingly sparse as you get
to bigger and bigger numbers.
So you are visiting fewer elements, and moreover,
you only have to do an addition to the get to the next element.
So you are doing less work per element.
And the nonmutating version is not as efficient
as the mutating version without a whole ton of work.
Let's bring it back to Cocoa.
So you see uses of immutability in the Cocoa,
Cocoa Touch frameworks.
There's a lot of them.
And that's Date, UI image, NSNumber, and so on.
These are immutable types,
and having these immutable types improves safety.
It's a good thing because you don't have
to worrying about copying.
You don't have to worry
about your sharing having unintended side effects.
But you also see the downsides there when you work with it.
I gave myself a little task in Objective-C.
I want to create an NSURL by starting with my home directory
and adding successive path components
to get to some directory.
And I wanted to do it without the mutation
in the reference semantic world.
So I created an NSURL.
Each time through the loop, I create a new URL
by appending the next path component.
This is not a great algorithm, really.
Every time through, I'm creating an NSURL, another object,
the old one goes away, and then the NSURL is going to copy all
of the string data each time through the loop.
Not very efficient there.
Doug, you are holding it wrong.
Really you should be collecting all these components
into an NSArray and then use file URL with path components.
Fine. But, remember, we are embracing immutability here.
So when I create my array, I will create an NSArray
with a particular object, all right,
that's the home directory.
Each time through, I create a new array,
adding one more object.
I'm still quadratic.
I'm still copying the elements.
I'm not copying the string data.
So it's a little better.
I'm still copying the elements.
This is why we don't fully embrace immutability
in the world of Cocoa because it doesn't make sense.
Instead, you use mutability
in more localized places where it makes sense.
Collect all of your components into an NSMutable array.
Then use file URL with path components to get back
to that immutable NSURL.
So immutability is a good thing.
It makes the reference semantic world easier to reason about.
But you can't go completely to immutability
or you start to go crazy.
So that brings us to value semantics.
With value semantics, we take a different approach.
We like mutation.
We think it's valuable.
We think it's easy to use when done correctly.
The problem, as we see it, is the sharing.
So you already know how value semantics work and you demand
on it all the time, whether you are in Objective-C or in Swift.
The idea is simple: if you have two variables,
the values in those variables are logically distinct.
So I have an integer A, I copy it over to an integer B.
Of course they are equivalent; it's a copy.
I go to mutate B.
If I told you that would change A, you would say I'm crazy.
These are integers.
They have value semantics
in every language we have ever worked with.
Go to CGPoint, for example.
Copy from A to B, mutate B, it's not going
to have any effect on A.
You are used to this.
If CGPoint didn't behave this way,
you would be really, really surprised.
The idea of value semantics is take this thing we already know
and understand for the very fundamental types, like numbers
and small structs containing numbers, and extend it outward
to work with much, much richer types.
So in Swift, strings are value types.
You create A, copy from B to A, go ahead and change B
in some way, it won't have any effect on A.
Because it's a value type.
A and B are different variables.
Therefore, they are logically distinct.
Okay? Why shouldn't the arrays behave exactly the same way?
Create A, copy it over to B, mutate B.
It has no effect on A.
They are completely distinct values.
Last one, well, dictionaries, of course.
It's just a collection.
You put value semantic things into it,
you get value semantics back.
The great thing here is that value types compose beautifully.
So you can build up very rich abstractions all in the world
of value semantics easily.
So in Swift, all the fundamental types -- integers, doubles,
strings, characters, et cetera -- they are all value types.
They have this fundamental behavior,
the two variables are logically distinct.
All the collections we build on top of them --
array, set, dictionary --
are value types when they're given value types.
And the language abstractions you use
to build your own types -- tuples, structs, and enums --
when you put value types into those, you get value types out.
Again, it's very, very easy to build rich abstractions all
in the world of value semantics.
Now, there's one more critical piece to a value type,
and that is that you have the notion of when two values,
two variables of value type, are equivalent.
They hold the same value.
And the important thing is that identity is not what matters.
Because you can have any number of copies.
What matters is the actual value that's stored there.
It doesn't matter how you got to that value.
I will tell you things that are really, really silly.
Here we have A, we set it to 5, and we have B
and we set it to 2 plus 3.
Of course A and B are equivalent.
You work with this all the time.
You couldn't understand integers if they didn't work this way.
So just extend that notion out.
Of course it's the same thing for CGPoints,
you wouldn't be able to understand them
if it wasn't this way.
Why shouldn't strings behave exactly the same way?
It doesn't matter how I get to the string "Hello, WWDC."
The string is the value, and the equality operator needs
to represent that.
You can make this arbitrarily crazy and stupid.
So here I will go and do some sorting operation.
What it comes down to though is that I have two arrays
of integers, the integers have the same values.
Therefore, these things are equivalent.
When you're building a value type, it's extremely important
that it conform to the Equatable protocol.
Because every value type out there should be equatable.
That means it has the equal equal operator
to do a comparison, but that operator has
to behave in a sensible way.
It needs to be reflexive, symmetric, and transitive.
Why are these properties important?
Because you can't understand your code unless you have them.
If I copy from A to B, well, I expect that A is equal
to B and B is equal to A.
Why wouldn't it be?
If I then copy B over to C, well then C and B and A,
they're all equivalent.
It doesn't matter which I have because the only thing
that matters there is the value, not the identity.
Fortunately, it's very, very easy to implement these things.
I can just say, take CGPoints and extend it with Equatable
and implement the equality operator,
and when you compose value types out of other value types,
generally speaking, you just have
to use the underlying equal, equal operators
of all the value types.
Let's bring this back to our temperature type.
We will make it a struct now.
We are going to switch Celsius back
to a var to we can mutate it.
This now has value semantics.
We give it the obvious equality operator.
We go ahead and use this in our example before.
We create the home, create the temperature, set the temperature
to 75 degrees Fahrenheit and whoa!
Compiler stops us here.
What went on?
Well, we are trying to mutate a property of temp,
which is describes as a let.
It's a constant.
It can't change.
We will appease the compiler.
Change it to var, and now we can mutate it.
And this all works perfectly fine.
Why is it fine?
Well, the house points out to the thermostat in the oven.
The thermostat and the oven both have their own instances
of temperature values.
They are completely distinct, they're never shared.
They also happen to be inlined into the struct,
and you are getting better memory usage and performance.
This is great.
Value semantics have made our lives easier here.
With our example, let's go all the way
and make everything value semantics.
Now, house is a struct that has a thermostat struct
and an oven struct, and the whole world is value semantics.
The changes we need to make
to our code is now home can be mutated because we can go ahead
and change the temperature on the thermostat of the home,
right, and that's a mutation to home
and thermostat and to temperature.
Okay. This brings us to a really important point.
Value semantics works beautifully in Swift
because of the way Swift's immutability model works.
When you have a let in Swift, it's of a value type.
It means this value will never change short
of something corrupting your process' memory.
This is a really strong statement.
It means it's very easy to reason
about things that are let.
But we still allow mutation.
You can use var to say this variable can be changed.
And that's extremely useful for our algorithms.
Note that this change is very local.
I can change this variable, but it's not going
to affect anything else anywhere in my program
until I tell it to, until I do a mutation somewhere else,
which gives you this really nice controlled mutability.
With strong guarantees elsewhere.
One of the nice things here is when you are using value types
of passing them across thread boundaries, it gives you freedom
from race conditions on those types.
So I create numbers.
I passed them off to some process
that will do something asynchronously.
I mutate numbers locally and then do it again.
With a reference semantic array, this is a race condition.
It's going to blow up on you sometime.
With value semantics, you are getting copies each time,
logical copies each time.
And therefore, there is no race condition.
They are not hitting the same array.
Hold on. This sounds like a performance problem, right?
We are doing a copy every time we pass numbers
through a parameter.
Okay. One of the important other pieces of value semantics is
that it copies are cheap.
By cheap, I mean constant time cheap.
Let's build this up from fundamentals.
So when you have the fundamental types,
the really low-level things -- integers, doubles, floats,
et cetera -- copying these is cheap.
You are copying a couple of bytes.
Usually it happens in the processor.
So then you start building structs
out of doubles and ints and so on.
Like CG points is built of two CG floats.
And any of these structs, enums or tuples,
they have a fixed number of fields, and copying each
of the things in there is constant time.
So copying the whole thing is constant time.
That's great for fixed-length things.
What about extensible things, strings, arrays,
dictionaries, and so on?
The way we handle these in the Swift world is by copy-on-write.
So this makes the copy cheap.
It's just some fixed number of reference-counting operations
to do a copy of a copy-on-write value.
And then at the point where you do a mutation, you have a var
and then you change it, then we make a copy
and work on the copy.
So you have sharing behind the scenes,
but it's not logical sharing
but logically these are still distinct values.
This gives you great performance characteristics
from value semantics and is really a nice programming model.
So we really love the value semantic programming model.
Different variables are logically distinct, always.
You have the notion of mutation, an efficient mutation,
when you want it locally controlled.
But you have these strong guarantees of let,
meaning it will not change elsewhere.
And copies are cheap, which makes us all work together.
With that, I would like to hand it over to my colleague,
Bill Dudney, who will talk about value types and practice.
BILL DUDNEY: Thanks, Doug.
So now that Doug has filled our minds with how value types work,
how they compare with reference semantics,
let's talk about building a real example that uses value types.
So what we are going to do is put together an example
where we build a simple diagram made
of a couple odifferent value types, a circle and a polygon.
So we will get started with the circle.
It's a center, and a radius.
A couple of value types that come straight
out of the standard library.
Of course, we want to implement the equality operator,
the equals equals operator,
and we do that by just comparing those types.
Again, since they are built into the standard library,
all we have to do is use those since we are composing
from the simple types that came out of the library.
Next up is the polygon.
It has an array of corners,
and each of the corners is just another CG point, which,
again, is a value type.
So our array is a value type, and, again,
our comparison is straightforward,
just using the equals, equals operator there
to make sure we implement the Equatable operator.
Now what we want to do is put these types into our diagram,
put both polygons and circles.
Making an array of circles is straightforward.
Making an array of polygons is straightforward.
So we can make an array of either type.
What we need to do is make one array that contains both.
The mechanism to do that in Swift is with a protocol.
So we will create a protocol called Drawable.
We will make both of our subtypes implement
that protocol, and then we can put them
into an array in our diagram.
Tons of great information
in this Protocol-Oriented Programming in Swift talk,
which is repeating again today at 3:30.
So if you haven't seen that,
I would highly suggest you take a look
at it or catch it on video.
So here's our Drawable protocol.
Straightforward and simple, has one method, Draw, on it.
And, of course, we want to implement that on our two types.
We will create an extension of polygon,
implement that draw method, and that's just going to call
out to Core Graphics and draw the polygon.
And the same thing for the circle.
So what we are going to do, just call Core Graphics to build
up the representation of the circle.
Now back to out diagram.
It's got this array of drawables called Items.
We need to create a method to add items.
That's marked as mutating because that mutates self.
We are going to implement the Draw method to simply iterate
through that list of items and call the Draw method
on each item that's in the list.
So let's take a look at it diagrammatically.
So we create a diagram, called doc.
We create a polygon and add that to the array.
We create another one, a circle, and we add that to the array.
Now our array has two drawables in it.
Notice they are different types.
When we create another document, by saying doc2 equals doc,
we get a logically distinct, brand-new instance.
It's logically separate from that first instance.
I can go back and make changes to doc2 now, and when I do that,
of course, it has no effect on doc.
I change that circle to a polygon.
The array has value semantics even though the collection
So it has the polygon inside, and the circle is inside
that array as a value.
So, of course, we want to make our diagram struct Equatable.
So we implement the protocol.
And this would be the straightforward implementation
that we would look at doing.
However, if we would do that, the compiler says, "Hey,
wait a minute, I don't have an equals equals operator
for these two values on either side of that equation."
Again, I will refer you
to the Protocol-Oriented Programming talk,
where we talked through all the details of how all that works.
In this talk, we will focus on the value semantics.
So drawable has a single method called Draw,
our diagram has a method called Draw.
So let's go ahead and turn our diagram into a drawable.
All we have to do is add that declaration to it.
Now our diagram quacks like a duck and is a duck.
So this brings us to an interesting point.
I can create a new diagram and add it to my existing diagram.
It's got three different types in there,
but they are all contained inside that array.
It's a new instance of Diagram.
But I can push it one further and add
that document to the array.
Now if this were reference semantics --
let's look at the Draw method.
If this were reference semantics,
this would infinite recurse.
As I call Draw on my diagram, it will go through the list
of items and find itself in that list.
And so it's going to call Draw again and infinitely recurse.
But, we are using values.
So instead of the doc being added to my diagram,
it's completely a separate
and distinct instance because it's a value.
So there's no infinite recursion.
I just get two polygons and two circles drawn.
Now that we have talked about building a tree
of objects purely out of value types,
let's talk about how we mix value types and reference types.
Now in Objective-C, you are used
to putting primitive data types inside your reference types all
This is how we build things in Objective-C.
But the flip side introduces some interesting questions
that we have to think through.
If we are building a value type, we want to make sure
that that value type maintains its value semantics,
even though it has a reference inside of it.
So if we are going to do that,
we have to think about that question.
How do we deal with this fact
that two different values might be pointing to the same thing
because it has a reference in it?
So we have to solve that question.
The other thing we have to think
through is how is equality affected by that.
So let's start with a simple example
with an immutable class, UIImage.
We will create an image struct that is going to be a drawable,
and it has a reference to a UIImage.
So we create the instance
with this beautiful photograph of San Francisco.
And if we create another one, image 2, so now image
and image 2 are both pointing to the same object.
And you look at this and you think, Bill is going
to trick us, and this is going to be a problem, and it will be
like the temperature thing.
But it's not because UIImage is immutable.
So we don't have to worry about image 2 mutating the image
that sits underneath it
and having the first image be caught off guard
with that change.
Of course we want to make sure we implement the equality
and at first blush, you might look at this and think, okay,
I will use the triple equals operator,
which will compare the reference and see
if those references are the same.
Well this would work okay in this example, but we also have
to think through what happens
if we create two UI images using the same underlying bitmap.
We want those also to equate to being equal, and in this case,
since we are comparing the reference, they would not.
So this would be falsely saying
that these two images are not the same.
So instead what we want to do is use the Is Equal method
that we inherit from NSObject on UIImage to do the comparison,
so we'll be sure that the reference type gets the right
answer on whether or not it's the same object or not.
Let's talk about using mutable objects.
We have a BezierPath here.
It also implements Drawable.
But its entire implementation is made
up by this mutable reference type, UIBezierPath.
In the reading case, when we're doing Is Empty,
everything is okay.
We are not doing any mutation, so we're not going
to mess any other instances up.
But on this one below, we have this Add Line To Point method,
and if we have two BezierPaths pointing to that,
it will cause problems.
Also notice here, we don't have the Mutating keyword there.
That's a sign that we know we are mutating because Add Line
To Point is there, but the compiler is not yelling
at us about it.
That's because path is a reference type.
We will look at that again in just a moment.
So if I have two instances of BezierPath, both pointing
to the same instance of UIBezierPath
through this reference, and I make this mutation, that's going
to catch the other one off guard.
This is a bad situation.
We are not maintaining value semantics.
We need to fix that.
The way we will fix that is use copy-on-write,
and we want to make sure that before we write to that path,
that we make a copy of it.
So to do that, we need to introduce a couple
of new things to our BezierPath.
First, we want to make our path instance private,
and next we want to implement this computed path property
for reading and from there we will return our private
And we want to make a path
for writing computed property that's marked as mutating,
and that's going to, in fact, change the state.
So we marked it mutating and we set path equal
to a new copy of our existing path.
Now we have both a reading copy and a way to get a writing copy.
And we change our implementation to reflect that.
In the Is Empty method, we will call our reading copy,
and in the mutating method below,
we will call the path for writing.
And the great thing about this that the compiler is going
to yell at us and say, "Hey,
that path for writing property is marked as mutating,
and this method is not marked for mutating."
So we are getting help from the compiler to help us figure
out when we are doing something wrong.
Just to look through it in a diagram, the path,
I create another one by saying Path To.
Of course, I can read from it.
No issue. And when I go to write to it,
since I'm creating another instance of BezierPath,
path two is none the wiser that a mutation has happened.
So I will not introduce some unexpected mutation behind
So now let's talk about how to use these things in practice.
Here we have our polygon type, and we are extending
that by adding a method that's going to return
to us a BezierPath that describes that polygon.
So we create the BezierPath, iterate through the points,
adding a line two to each of these points.
Now, the downside is remember that Add Line
To Point method is copying on every call.
So this is not going to perform as well as it might.
So instead, what we should do is create an instance
of UIBezierPath and mutate that mutable reference type in place
and when we're done, create a new instance of our value type
with that BezierPath and return that.
That creates only one copy or only one instance
of UIBezierPath instead of multiple.
In Swift, we have a great feature where we know
if objects are uniquely referenced,
and so we can take advantage of that.
This is a similar structure to what we saw in our BezierPath,
and we can use this fact that we have this uniquely referenced
property and we know for a fact
that something is uniquely referenced
so we can avoid making the copies if we know
that that reference type is uniquely referenced.
The standard library uses that feature throughout
and does a lot of great performance optimizations
So that's mixing value types and reference types.
You want to make sure that you maintain value semantics despite
the fact that you have these references to mutable types
by using copy-on-write.
So now I want to look
at a really cool feature we can do now
that we have a model type implemented as a value,
and implement an undo stack.
So I'm going to create a diagram and an array of diagrams.
Then with every mutation, I will add my doc to my diagram array.
So I create it and append.
I add a polygon and append it to the undo stack.
I create a circle and append that to the undo stack.
Now in my undo stack I have three distinct instances
These are not references to the same thing,
these are three distinct values.
And so, I can implement some really cool features with this.
So imagine this in an app, and I have a History button.
I tap on the History button and I get the list of all the states
of my diagram back through my undo stack.
I can allow the user to tap on something
and essentially go back in time.
I don't have to keep anything in some array of how to undo adding
that property or anything.
It just goes back to that previous instance,
and that's the one that gets drawn.
This is a super-powerful feature, and, in fact,
Photoshop uses this extensively to implement all
of their history stuff.
When you open an image in Photoshop,
what happens behind the scenes?
Photoshop slices and dices that photo,
no matter how large it is, into a bunch of small tiles.
Each of those tiles are values, and the document
that contains the tiles is also a value.
Then if I make a change, like change this person's shirt
from purple to green, the only thing that gets copied
in the two instances of that diagram are the tiles
that contain the person's shirt.
So even though I have two distinct documents,
the old state and the new state, the only new data
that I have had to consume as a result
of that is the tiles contained in this person's shirt.
So in summary, we have talked about value types,
and what great features they bring to your applications,
compared that to reference types and showed how value types fix
up some of those issues.
We talked through an example and saw some cool features
that you can add to your applications
by using value types.
I look forward to seeing how those things play
out in your apps.
Some related sessions that you can catch on video
or if you have time today at 3:30
for the protocol-oriented talk.
For more information, you can always email Stephan or go
to our forums, and the documentation also has
Thank you very much and I hope the rest of your WWDC is great.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.