
@attheodo
Demystifying Swift keywords you use every day
Agenda

-
Closures and value-capturing
-
weak/self/unowned
-
-
@autoclosure
-
Metatypes
Closures

There are two types of closures...
-
@escaping
-
Can be stored, passed around to other closures, get executed in the future...
-
-
@noescape
-
They execute the code immediately and cannot be run later or passed over...
-
By default, closures in swift are marked as @noescape!
Closures

There are two types of closures...
-
@escaping
-
@noescape
DispatchQueue.main.async {
print("Async in main thread")
}
[1, 2, 3].forEach { number in
print(number)
}
Closures & Value capturing

-
When we use a closure, most of the time, we need to use variables from the surrounding context
-
We are able to use these variables by capturing them inside the closure.
getUser(withId: id) { [userRepository] result in
userRepository.addUsers(result.users)
}
Capture list
Closure arg
getUser(withId id: String,
completion: @escaping RequestResult<User>)
{
...
completion(.success(users))
}
Closures & Value capturing

struct Calculator {
var a: Int
var b: Int
var sum: Int {
return a + b
}
}
Closures & Value capturing

let calculator = Calculator(a: 3, b: 5)
let closure = {
print("The result is \(calculator.sum)")
}
closure() // "The result is 8"
calculator.a = 100
closure() // "The result is 105"
Closure without capturing calculator
Closures & Value capturing

var calculator = Calculator(a: 3, b: 5) // 0x618000220400
let closure = {
calculator = Calculator(a: 33, b: 55) // 0x610000221be0
}
print(calculator) // calculator has address 0x618000220400
closure()
print(calculator) // calculator has address 0x610000221be0
Closure without capturing calculator
Closures & Value capturing

Calculator "leaks" outside the scope of the closure...

Calculator
closure
Closures & Value capturing

We need to "capture" calculator, so that the closure has an immutable copy only... and changes outside the closure's scope won't affect the "local" copy...
let calculator = Calculator(a: 3, b: 5)
let closure = { [calculator]
print("The result is \(calculator.sum)")
}
closure() // "The result is 8"
calculator.a = 100
closure() // "The result is 8"
Closures & Value capturing

You can capture multiple reference or value types in
closures...
let closure = { [user, userRepository] item in
userRepository.add(user, item: item)
}
... or even create aliases for them...
let closure = { [face = user, repo = userRepository] in
repo.add(face)
}
Closures & Value capturing

Implicit use of 'self' in closure; use 'self.' to
make capture semantics explicit

Closures & Value capturing

Relax. The compiler just wants to save you from trouble...

Closures & Value capturing

... cause now, your might have a retain cycle!

Closures & Value capturing

- Remember @escaping?
- self might actually "escape" to who knows where event though its original instance should be long gone...
- Closures by default, create a strong reference to their captured values!
- Enter weak/unowned
Closures & Value capturing

let closure = { [weak self] item in
self?.addItemToInventory(item)
}
- weak turns every captured value into an optional
let closure = { [unowned self] item in
self.addItemToInventory(item)
}
-
unowned turns every captured into an explicitly unwrapped optional ☢️
- very, very, very unsafe
- Both weak & unowned, allow you to avoid retain cycles.
Closures & Value capturing




@autoclosure

-
@autoclosure attribute enables you to define an argument that automatically gets wrapped in a closure.
-
It’s primarily used to defer execution of a (potentially expensive) expression to when it’s actually needed, rather than doing it directly when the argument is passed.
-
But in day to day coding, let's say it just makes some stuff a little more readable...
@autoclosure

func animate(_ animation: @escaping () -> Void) {
UIView.animate(withDuration: duration, animations: { animation() })
}
func animate(_ animation: @autoclosure @escaping () -> Void)
UIView.animate(withDuration: duration, animations: animation)
}
Metatypes

String.Self vs String.Type
-
A metatype type refers to the type of any type (including class, structures, enums and protocols)
-
The name of that type is the name of that type followed by .Type
-
SomeClass → SomeClass.Type
-
SomeProtocol → SomeProtocol.Protocol
-
-
Instance.self returns just the value of the Metatype
Metatypes

- The Self type isn't a specific type. It rather lets you conveniently refer to a type without knowing it or repeating it.
- It can only appear:
- As a return type of a method
- As a return type of a subscript
- As a return type of a read only property
- In the body of a method
- Self type refers to the same type returned by type(of:) (which is always a Metatype)
Metatypes

func printType<T>(of type: T.Type) {
// or you could do "\(T.self)" directly and
// replace `type` parameter with an underscore
print("\(type)")
}
printType(of: Int.self) // this should print Swift.Int
class Superclass {
func f() -> Self { return self }
}
let x = Superclass()
print(type(of: x.f())) // Prints "Superclass"
Metatypes

- Generic, street-smart, rule-of-thumb:
- Use .Type when declaring parameters
- Use .self when passing arguments
- Use Self mostly when "returning"
Questions?


Demystifying Swift keywords
By Thanos Theodoridis
Demystifying Swift keywords
- 222