Skip to content

Commit

Permalink
feat(blog): add swift-value-reference
Browse files Browse the repository at this point in the history
  • Loading branch information
theodorusclarence committed May 10, 2022
1 parent fe163fe commit 9454631
Show file tree
Hide file tree
Showing 2 changed files with 349 additions and 3 deletions.
346 changes: 346 additions & 0 deletions src/contents/blog/swift-value-reference.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,346 @@
---
title: "Understanding Swift's Value and Reference Types"
publishedAt: '2022-05-10'
description: "In-depth explanation of Swift's value and reference types."
englishOnly: 'true'
banner: 'rafael-garcin-VR03NcIer2U-unsplash_lpjftc'
tags: 'swift'
---

## Introduction

There are **two kinds of types** in Swift, which are Value and Reference Types. These types and their characteristics sometimes can be hard to remember and understand. Through this post, I'll try to explain it using a mental model and analogy which will help you easily master swift types.

## Primitives

If you use common programming languages (Java, JavaScript, etc) before, you must be familiar with **primitives and non-primitives data types**. I'm not going to jump into the details of primitives, but here are some illustrations I got from google.

<CloudinaryImg
mdx
publicId='theodorusclarence/blogs/swift-value-reference/other-primitive-types_vfmjxc'
alt='other-primitive-types'
width={2505}
height={720}
/>

Usually, primitives conclude specific data types such as boolean, char, integer, and float.

### Does Swift have primitive types?

No, **Swift doesn't have primitive types.** In a sense. Swift **still provides 'primitive-like' data** types such as Int, Bool, Double, etc. However, they are made with **struct**.

If you look into Swift's `Int` type definition, you can see that it is made with a `struct`

<CloudinaryImg
mdx
publicId='theodorusclarence/blogs/swift-value-reference/primitive-made-with-struct_k2psql'
alt='primitive-made-with-struct'
width={768}
height={449}
/>

Interesting right?

---

## Quick Intro to Mental Model

> A mental model is an explanation of someone's **thought process** about how something works in the real world. It is a **representation of the surrounding world**. - [Wikipedia](https://en.wikipedia.org/wiki/Mental_model)
You might be familiar with this variable box analogy:

<CloudinaryImg
mdx
publicId='theodorusclarence/blogs/swift-value-reference/box-analogy_ay0zra'
alt='box-analogy'
width={1400}
height={436}
/>

> We think of variables as containers that hold information and allow us to access them later. We will think of this as a **box** that has a **label** on it. - [StudeApps](https://studeappsblog.medium.com/what-is-a-variable-dd7e539bf388)
This works wonders when you are trying to understand what a variable does.

**That is a mental model.** You create a certain type of analogy to help you understand a concept.

The prior analogy is not a one-size-fits-all, I won't be using it to explain value & reference type. So prepare for some changes 💪

---

## Value and Reference Types

<CloudinaryImg
mdx
publicId='theodorusclarence/blogs/swift-value-reference/types-in-swift_culrrx'
alt='types-in-swift'
width={1302}
height={596}
/>

There are two kinds of types in Swift which are **Value Types**, and **Reference Types.** Value types are usually defined as `struct`, `enum`, and `tuple`. Whereas the latter is usually defined as a `class`

### Wire Analogy

I'm going to use a new mental model for variables, which uses a wire to **point** to the value it holds.

<CloudinaryImg
mdx
publicId='theodorusclarence/blogs/swift-value-reference/wire-analogy_b0nblr'
alt='wire-analogy'
width={1155}
height={497}
/>

Therefore each variable can point to a single value according to its data type.

---

## Value Types

<CloudinaryImg
mdx
publicId='theodorusclarence/blogs/swift-value-reference/value-types_ofai6i'
alt='value-types'
width={736}
height={550}
/>

> A value type is a type whose value is **copied** when it's assigned to a variable or constant, or when it's passed to a function - [Swift Docs](https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html)
Remember that 'primitive' data types like Int, Double, String, etc. are made with **struct.** So they follow the value type mental model.

### Mental Model

Let's say we have a struct of Animal (the behavior is also the same with enum, tuple, also Int, String because they're made with struct)

```swift
struct Animal {
var legs = 4
}

var sheep = Animal()
```

<CloudinaryImg
mdx
publicId='theodorusclarence/blogs/swift-value-reference/types-model-1_jwbgaz'
alt='types-model-1'
width={577}
height={190}
/>

Then, we are assigning the `cow` variables with the value of `sheep`

```swift
var cow = sheep
```

Key point: **the value will be copied**.

<CloudinaryImg
mdx
publicId='theodorusclarence/blogs/swift-value-reference/types-model-2_uguz9k'
alt='types-model-2'
width={612}
height={695}
/>

### Effect of Copying

After we copy, the `sheep` and `cow` variables now points to **two different struct.** Therefore if we mutate the `cow`, the `sheep` **won't get affected**, and vice versa.

```swift {8,9,11,12}
struct Animal {
var legs = 4
}

var sheep = Animal()
var cow = sheep

// mutating cow's property
cow.legs = 3

print(sheep.legs) // 4
print(cow.legs) // 3
```

<CloudinaryImg
mdx
publicId='theodorusclarence/blogs/swift-value-reference/types-copy-effect_g8v4gn'
alt='types-copy-effect'
width={751}
height={458}
/>

## Reference Types

<CloudinaryImg
mdx
publicId='theodorusclarence/blogs/swift-value-reference/reference-types_cwxcvo'
alt='reference-types'
width={723}
height={647}
/>

> A reference types is where instances **share a single copy** of the data when they're assigned to a variable or constant, or when they're passed to a function.
In the wire analogy, **it will point to the same value**. We're using a class that behaves as a reference type.

### Mental Model

```swift
class Animal {
var legs = 4
}

var sheep = Animal()
var cow = sheep
```

Key Point: **It will share a single copy**

<CloudinaryImg
mdx
publicId='theodorusclarence/blogs/swift-value-reference/reference-model_oj5b3u'
alt='reference-model'
width={584}
height={659}
/>

### Proof

To prove that it is sharing a single copy, we can use `===` ([identity equality](https://developer.apple.com/documentation/swift/1538988)). It will return true if two reference point to the same object instance.

Let's throw in a new instance called `pig`

```swift
var sheep = Animal()
var cow = sheep

// created a new instance
var pig = Animal()
```

Here's the wire

<CloudinaryImg
mdx
publicId='theodorusclarence/blogs/swift-value-reference/reference-proof_pkg0tg'
alt='reference-proof'
width={549}
height={448}
/>

Then we can compare them using identity equality

```swift
print(sheep === cow) // true
print(sheep === newSheep) // false
```

When in doubt, draw the wire analogy to help you. I'm using [excalidraw](https://excalidraw.com/) for the illustration

### Effect of Sharing A Single Copy

I believe you already guessed correctly how it will behave. If we **mutate** one variable, **both will be affected**.

```swift {8,9,11,12}
class Animal {
var legs = 4
}

var sheep = Animal()
var cow = sheep

// mutating cow's property
cow.legs = 3

print(sheep.legs) // 3
print(cow.legs) // 3
```

<CloudinaryImg
mdx
publicId='theodorusclarence/blogs/swift-value-reference/reference-copy-effect_khsni9'
alt='reference-copy-effect'
width={824}
height={444}
/>

## Additional Emphasize

I need to emphasize this in case you're coming from **a JavaScript** background.

In Swift, **Array and Dictionary are all value types**.

<CloudinaryImg
mdx
publicId='theodorusclarence/blogs/swift-value-reference/array-struct_xfz7eu'
alt='array-struct'
width={772}
height={332}
/>

It is still made with struct 😬

## How to Choose?

I don't have much experience with this yet, so I'll [quote an article](https://developer.apple.com/swift/blog/?id=10) instead

Use a value type when:

- Comparing instance data with `==` makes sense
- You want copies to have an independent state
- The data will be used in code across multiple threads

Use a reference type (e.g. use a class) when:

- Comparing instance identity with `===` makes sense
- You want to create a shared, mutable state

I believe that using value type for overall use will be sufficient. We can trust that when we change one variable/property, it won't affect the others. Thus, creating **a sense of safety and reliability**.

Keep a note that this difference only happens when you mutate. **In absence of mutation, values and references act exactly the same way.**

## Functions & In-Out

Function parameter follows **value types.** This means you can't mutate the parameter and change the value.

<CloudinaryImg
mdx
publicId='theodorusclarence/blogs/swift-value-reference/function-unable-to-mutate_jk9sbh'
alt='function-unable-to-mutate'
width={744}
height={116}
/>

**Swift won't even let you mutate them.** Because what is passed in the parameter will be converted into a `let` variable.

You can **imitate reference types** on function parameter by using `inout`

```swift
var numbers = [1,2,3]

func foo(_ arr: inout Array<Int>) {
arr.removeLast()
}

foo(&numbers)
print(numbers) // [1,2]
```

Notice the `&`(ampersand) which is an explicit recognition that you're aware it is being used as `inout`.

Under the hood, the In-Out parameter **doesn't use reference types.**

> This behavior is known as _copy-in copy-out_ or _call by value result_. For example, when a computed property or a property with observers is passed as an in-out parameter, its getter is called as part of the function call and its setter is called as part of the function return. - [Swift Docs](https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID545)
## Conclusion

You now understand that:

- Swift 'primitive-like' variables are made with a struct
- Value types will copy the value if assigned to a variable or passed into a function
- Reference types will share a single instance if assigned to a variable or passed into a function
- Mutating value types won't affect the other copy, on the other hand, mutating reference types will affect the single instance
- Function parameters follows value types, but can imitate reference types by using the in-out parameter
6 changes: 3 additions & 3 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -347,12 +347,12 @@ export async function getStaticProps() {
const library = await getAllFilesFrontmatter('library');

const featuredPosts = getFeatured(blogs, [
'one-stop-starter',
'2021-retrospective',
'swift-value-reference',
'nextjs-storybook-tailwind',
'react-core-concept-rendering-state',
'nextjs-fetch-method',
'youtube-list',
'one-stop-starter',
'2021-retrospective',
]);
const featuredProjects = getFeatured(projects, [
'seasforus',
Expand Down

1 comment on commit 9454631

@vercel
Copy link

@vercel vercel bot commented on 9454631 May 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.