connect.tech- swift memory management
TRANSCRIPT
![Page 1: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/1.jpg)
Swift Memory ManagementBy: Marcus Smith Software Engineer stable|[email protected]
![Page 2: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/2.jpg)
@stablekernel
Why should you care?
![Page 3: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/3.jpg)
@stablekernel
Who am I?
• Marcus Smith • iOS Developer at stable|kernel • Struggled learning memory management • Previously worked for BMW and Frozen Fire Studios • Have a game in the app store
![Page 4: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/4.jpg)
stable|kernel is an Atlanta-based mobile development company to craft smartly-designed mobile applications that connect brands directly with their users. Our team of engineers and designers takes clients from strategy through design, development and deployment, ensuring timely delivery of the highest quality applications.
![Page 5: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/5.jpg)
Memory Management
@stablekernel
• Value & Reference Types • Reference Counting & ARC • Avoiding memory issues
![Page 6: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/6.jpg)
Value & Reference Types
@stablekernel
• Value types contain data • Reference types contain the location in memory where data is
![Page 7: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/7.jpg)
Value Types
@stablekernel
• Structs, Enums and Tuples • Copied on assignment or when passing to a function
struct Point { var x, y: Int }
let aPoint = Point(x: 0, y: 0) var anotherPoint = aPoint anotherPoint.x = 5 print(aPoint.x) // prints 0
![Page 8: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/8.jpg)
Reference Types
@stablekernel
• Classes and Closures • Creates another reference when assigned to a variable or passed to a function • Implicit sharing
![Page 9: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/9.jpg)
@stablekernel
class Person { var name: String var age: Int init(name: String, age: Int) {…} }
let bob = Person(name: “Bob”, age: 30) let myNeighbor = bob myNeighbor.age = 50 print(bob.age) // prints 50
func increaseAge(person: Person) { person.age += 1 }
increaseAge(person: bob) print(myNeighbor.age) // prints 51
![Page 10: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/10.jpg)
Reference Types
@stablekernel
• More power, more responsibility • Can have multiple owners • Inheritance • Require some memory management on the part of the programmer
• blog.stablekernel.com
![Page 11: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/11.jpg)
Reference Counting
@stablekernel
• Type of garbage collection • Objects keep a count of references • When an object’s reference count gets down to 0, it is destroyed and its
memory is freed • Used to be done manually with alloc, retain, copy, release
![Page 12: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/12.jpg)
@stablekernel
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html
![Page 13: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/13.jpg)
Automatic Reference Counting (ARC)
@stablekernel
• Compiler makes retain and release calls • Has a problem with circular references creating retain cycles (memory leaks)
![Page 14: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/14.jpg)
@stablekernel
class Person { var name: String var age: Int init(name: String, age: Int) {…} var apartment: Apartment? }
class Apartment { let unit: String init(unit: String) {…} var tenant: Person? }
class SomeViewController: UIViewController { var person: Person!
override func viewDidLoad() { super.viewDidLoad()
person = Person(name: “Bob”, age: 30) let unit4A = Apartment(unit: “4A”) person.apartment = unit4A unit4A.tenant = person } }
![Page 15: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/15.jpg)
@stablekernel
UINavigationController SomeViewController
Person
Apartment
Push1
1
1
2
0Pop
![Page 16: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/16.jpg)
ARC - Strong and Weak References
@stablekernel
• ARC deals with circular references by having strong and weak references • Strong references increase an object’s reference count • Weak references do not increase an object’s reference count • In Swift, references are strong by default
class Apartment { let unit: String init(unit: String) {…} var tenant: Person? }
![Page 17: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/17.jpg)
ARC - Strong and Weak References
@stablekernel
• ARC deals with circular references by having strong and weak references • Strong references increase an object’s reference count • Weak references do not increase an object’s reference count • In Swift, references are strong by default
class Apartment { let unit: String init(unit: String) {…} weak var tenant: Person? }
![Page 18: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/18.jpg)
@stablekernel
UINavigationController SomeViewController
Person
Apartment
Push1
1
1
0Pop
0
0
![Page 19: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/19.jpg)
ARC - Weak References
@stablekernel
• Weak references help prevent retain cycles • Can go nil at any time, which was problematic in Objective-C • In Swift, weak references must be optional vars
weak var tenant: Person
weak let tenant: Person? ‘weak’ must be a mutable variable, because it may change at runtime
‘weak’ variable should have optional type ‘Person?’
![Page 20: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/20.jpg)
ARC - Unowned References
@stablekernel
• Like weak references, but non-optional • Should only be used if you can guarantee the object won’t become nil • Objective-C name “Unsafe Unretained” • In Objective-C dangling pointers could cause crashes or data corruption
“Swift guarantees your app will crash if you try to access an unowned reference after the instance it references is deallocated. You will never encounter unexpected behavior in this situation. Your app will always crash reliably, although you should, of course, prevent it from doing so.”
unowned let tenant: Person
![Page 21: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/21.jpg)
Avoiding Retain Cycles
@stablekernel
• Delegates • Parent - Child relationships • IBOutlets • CoreData • Closures • Lazy properties
![Page 22: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/22.jpg)
Delegates
@stablekernel
• Delegates should be weak open class UITableView { weak open var dataSource: UITableViewDataSource? weak open var delegate: UITableViewDelegate? ... }
protocol SomeDelegate {}
class SomeObject { weak var delegate: SomeDelegate? } ‘weak’ may only be applied to class and class-bound
protocol types, not ‘SomeDelegate’
![Page 23: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/23.jpg)
Delegates
@stablekernel
• Delegates should be weak open class UITableView { weak open var dataSource: UITableViewDataSource? weak open var delegate: UITableViewDelegate? ... }
protocol SomeDelegate: class {}
class SomeObject { weak var delegate: SomeDelegate? } ‘weak’ may only be applied to class and class-bound
protocol types, not ‘SomeDelegate’
![Page 24: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/24.jpg)
Parent - Child Relationships
@stablekernel
• Parents typically should hold a strong reference to their children • Children should hold a weak reference to their parent
extension UIView { open var superview: UIView? { get } open var subviews: [UIView] { get } ... }
open class UIViewController { weak open var parent: UIViewController? { get } open var childViewControllers: [UIViewController] { get } ... }
![Page 25: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/25.jpg)
IBOutlets
@stablekernel
• Weak by default (except root view) • Retained by superviews • Need to be declared as strong if you will be removing and re-adding • Same is true for NSLayoutConstraint
![Page 26: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/26.jpg)
@stablekernel
class ViewController: UIViewController { @IBOutlet weak var someView: UIView! /// A subview of `someView` @IBOutlet weak var someSubView: UIView! override func viewDidLoad() { super.viewDidLoad() someView.removeFromSuperview() }
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) someSubView.backgroundColor = .red }}
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
fatal error: unexpectedly found nil while unwrapping an Optional value
![Page 27: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/27.jpg)
@stablekernel
class ViewController: UIViewController { @IBOutlet var someView: UIView! /// A subview of `someView` @IBOutlet weak var someSubView: UIView! override func viewDidLoad() { super.viewDidLoad() someView.removeFromSuperview() }
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) someSubView.backgroundColor = .red }}
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
fatal error: unexpectedly found nil while unwrapping an Optional value
![Page 28: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/28.jpg)
Core Data
@stablekernel
• CoreData relationships on NSManagedObjects create retain cycles • To break these retain cycles call refresh on NSManagedObjectContext:
context.refresh(someManagedObject, mergeChanges: false)
![Page 29: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/29.jpg)
Closures
@stablekernel
• Blocks of code that can be passed around
let sayHello: (String) -> Void = { name in print("Hello \(name)") } sayHello("World") // prints "Hello World”
let add: (Int, Int) -> Int = { x, y in return x + y } let total = add(3, 5) // returns 8
let numbers = [1, 2, 3, 4, 5] let odds = numbers.filter { number in return number % 2 == 1 } print(odds) // prints [1, 3, 5]
![Page 30: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/30.jpg)
Closures
@stablekernel
• Reference type • Have to capture and store references to the variables they need to run
let increaseBobsAgeBy: (Int) -> Void = { (value) in bob.age += value }
class Executable { let bob: Person init(bob: Person) {…} func execute(value: Int) { bob.age += value } }
increaseBobsAgeBy(2)
![Page 31: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/31.jpg)
Closures - Capturing Self
@stablekernel
class SomeClass { var completion: () -> Void func doSomething() {…} func makeNetworkRequest() {…} func update() { completion = { doSomething() } makeNetworkRequest() } }
Call to method ‘doSomething’ in closure requires explicit ‘self.’ to make capture semantics explicit
![Page 32: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/32.jpg)
Closures - Capturing Self
@stablekernel
class SomeClass { var completion: () -> Void func doSomething() {…} func makeNetworkRequest() {…} func update() { completion = { self.doSomething() } makeNetworkRequest() } }
Call to method ‘doSomething’ in closure requires explicit ‘self.’ to make capture semantics explicit
• Closures require an explicit self • Can cause retain cycles if an object owns a closure that captures itself
![Page 33: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/33.jpg)
Closures - Capturing Self
@stablekernel
class SomeClass { var completion: () -> Void func doSomething() {…} func update() { completion = { self.doSomething() } } } class Executable { let someClass: SomeClass init(someClass: SomeClass) {…} func execute() { someClass.doSomething() } }
![Page 34: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/34.jpg)
Closures - Capturing Self
@stablekernel
class SomeClass { var completion: () -> Void func doSomething() {…} func update() { completion = { [weak self] in self.doSomething() } } } class Executable { let someClass: SomeClass init(someClass: SomeClass) {…} func execute() { someClass.doSomething() } }
![Page 35: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/35.jpg)
Closures - Capturing Self
@stablekernel
class SomeClass { var completion: () -> Void func doSomething() {…} func update() { completion = { [weak self] in self.doSomething() } } } class Executable { weak var someClass: SomeClass? init(someClass: SomeClass) {…} func execute() { someClass.doSomething() } }
![Page 36: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/36.jpg)
Closures - Capturing Self
@stablekernel
class SomeClass { var completion: () -> Void func doSomething() {…} func update() { completion = { [weak self] in self?.doSomething() } } } class Executable { weak var someClass: SomeClass? init(someClass: SomeClass) {…} func execute() { someClass?.doSomething() } }
![Page 37: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/37.jpg)
Closures - Capturing Self
@stablekernel
class SomeClass { var completion: () -> Void func doSomethingWithNonOptional(_ someClass: SomeClass) {…} func update() { completion = { [weak self] in } } }
![Page 38: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/38.jpg)
Closures - Capturing Self
@stablekernel
class SomeClass { var completion: () -> Void func doSomethingWithNonOptional(_ someClass: SomeClass) {…} func update() { completion = { [unowned self] in doSomethingWithNonOptional(self) } } }
![Page 39: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/39.jpg)
Closures - Capturing Self
@stablekernel
class SomeClass { var completion: () -> Void func doSomethingWithNonOptional(_ someClass: SomeClass) {…} func update() { completion = { [weak self] in if let strongSelf = self { doSomethingWithNonOptional(strongSelf) } } } }
![Page 40: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/40.jpg)
Closures - Capturing Self
@stablekernel
class SomeClass { var completion: () -> Void func doSomethingWithNonOptional(_ someClass: SomeClass) {…} func update() { completion = { [weak self] in guard let strongSelf = self else { return } doSomethingWithNonOptional(strongSelf) } } }
![Page 41: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/41.jpg)
Closures - Capturing Self
@stablekernel
• Self does not always need to be captured weakly • Depends on what objects may own the closure with self
DispatchQueue.main.async { self.doSomething() // Not a retain cycle }
UIView.animate(withDuration: 1) { self.doSomething() // Not a retain cycle }
DispatchQueue.main.asyncAfter(deadline: .now() + 30) { self.doSomething() // Not a retain cycle, but... }
![Page 42: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/42.jpg)
Lazy Properties
@stablekernel
• Lazy closure properties need to capture self weakly to prevent retain cycles
class Person { lazy var greeting: () -> String = { [unowned self] in return “Hi, I’m \(self.name)” } }
class Person { lazy var fullName: String = { return “\(self.firstName) \(self.lastName)” }() }
• Other lazy properties do not
![Page 43: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/43.jpg)
Avoiding Retain Cycles
@stablekernel
• Delegates - delegate should be weak • Parent - Child relationships - parent property should be weak • IBOutlets - weak or strong is fine, but strong if removing and re-adding • CoreData - refresh changes to turn back into faults • Closures - be careful what you capture and how • Lazy properties - only need to capture self weakly for closure properties
![Page 44: Connect.Tech- Swift Memory Management](https://reader031.vdocument.in/reader031/viewer/2022011722/587123801a28abe4448b59a5/html5/thumbnails/44.jpg)
Questions?
Business Inquiries: Sarah Woodward Director of Business Development [email protected]
Marcus Smith Software Engineer [email protected] blog.stablekernel.com