doyeona
Published in

doyeona

Automatic Reference Counting in Swift [ARC, weak, strong, unowned]

Previously, I posted a summary of the differences between structures and classes and i’m so glad that many of you like my posts! 💜

if you haven’t read my previous post about “structures and classes” yet, please go and read it first before get to know what ARC is for better understanding if you are new to swift. 👇🏻

As i said previously, CPU is not managing heap memory (reference type such as class) automatically and that’s what ARC exists for to prevent memory leak.

value types which store on stack memory such as structures and enumerations are just copying the data to data. therefore it’s not affected by ARC.

Automatic Reference Counting (ARC) is to track and manage the app’s memory usage. ARC automatically frees up the memory used by class instances when those instances are no longer needed.

How Automatic Reference Counting Works?

Every time creating a new class instance, ARC allocates a chunk of memory to store information about that instance and when it’s no longer needed, ARC frees up the memory used by that instance so that the memory can be used for other purposes instead.

So how is it going to keep track of the instances?

Every instance of a class has a property called reference count so if reference count is greater than 0, the instance is still kept in memory otherwise, it will be removed from the memory.

ARC in Action

Let’s look at the simple example below.

class Person {
var name: String
init(name: String) {
self.name = name
print(“\(name) is being initialized”)
}
deinit {
print(“\(name) is being deinitialized”)
}
}
var reference1: Person?
var reference2: Person?

The person class has an initializer and deinitializer and both prints a message when the instance of the class is being called. The two variables below define optional Person type.

Next, we’ll initialize reference1 by assigning it an instance of the Person class and set its name property below

reference1 = Person(name: "John Appleseed")

so when you run the code, the output will be “John Appleseed is being initialized

So now, we will assign the same Person instance reference2 to reference1.

reference2 = reference1

and what if we set reference 1 to nil? Do you think that is going to print out “John Applessed is being deinitialized”? the answer is NO 😈.

reference1 = nil

because both instances share a reference to the same Person instance and even though variable reference1 is set to nil, the reference2 variable is still referring to the instance of Person because reference2 is still holding the Person’s instance.

reference2 = nil

after assigning reference2 to nil, it will finally print out “John Appleseed is being deinitialized”. In other words, the reference count became zero.

These are simple examples of the strong reference. a strong reference is a default reference and it keeps a firm hold on that instance, and does not allow it to be deallocated for as long as that strong reference remains.

so this is how ARC works.

but what if both instances are referring to each other?

reference1 = Person(name: "John Appleseed")reference2 = Person(name: "Doyeon Kim")do {     reference1?.name = reference2!.name     reference2?.name = reference1!.name}

The output of the code would be,

John Appleseed is being initializedDoyeon Kim is being initialized

which means that deinit never called for both objects and they just leaked.

How to solve this problem above?

swift provides two ways to resolve strong reference cycles by using weak or unowned keywords. Unlike strong references, they do not affect an instance’s reference count.

  • weak: is a reference that does not keep a strong hold on the instance it refers to, and so does not stop ARC from disposing of the referenced instance. That’s why the property must be an optional.
  • unowned: Like a weak reference, an unowned reference doesn’t keep a strong hold on the instance as well however, it is used when the other instance has the same lifetime or a longer life time which means expected to have a value and cannot become nil.
weak var reference1: Person?
var reference2: Person?

Then deinit will be called and you’ll see a message in console like below and reference1 will be immediately deallocated because variable ‘reference1’ is ‘weak’.

Doyeon Kim is being deinitialized

When is weak used in iOS?

  1. Delegation: it’s common pattern and it could cause an error if there’s something wrong. Therefore always declare delegation as weak.
  2. Closures: closure captures references for an object in the surrounding scope and does it automatically without any notice. When the closure outlives the scope it was declared in, it can strongly hold onto the object from that scope. if yes, declare the variable as weak or unowned.

Strong Reference Cycles for Closures

A strong reference cycle can also occur if you assign a closure to a property of a class instance, and the body of that closure captures the instance.

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let alert = UIAlertController(title: "Title", message: "Message", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "Done", style: .cancel, handler: { _ in
self
.doSomething()
}))present(alert, animated: true, completion: nil)
}
private func doSomething(){}

In the above code, it’s a simple code that presents an alert and calling doSomething() method. The closure captures a reference type from its surrounding scope, the closure creates a strong reference to the captured instance. This increases the instance’s retain count, which means that the instance is still stored in the memory so we can add [weak self] to break the cycle.

alert.addAction(UIAlertAction(title: "Done", style: .cancel, handler: {[weak self] _ in
self?
.doSomething()
}))

since weak must be an optional type, make self type as optional as well. Here’s another example using delegation,

protocol SomeDelegate: AnyObject {
func didTapthing()
}
class Foo: SomeDelegate{
func didTapthing() {
}
init() {
let vc = ViewController()
vc.delegate = self
}
}
class ViewController: UIViewController{
weak var delegate: SomeDelegate?
}

Above code creates a link between 2 instances like associating parent ViewController. For example, Class Foo conforms to SomeDelegate protocol and init() method is simply initializing vc as an instance of ViewController(). however, when you try to access its variable delegate in ViewController and assign it to self, it needs to have a keyword weak to avoid strong reference cycles.

It seems hard to get fully understood at first, but most of the time we don’t really have to worry about the memory management because swift does it for us but those two above cases are the most common cases to think and work with memory carefully.

Thank you for reading my post and please let me know if there’s something to improve my iOS development knowledge!👏🏻👏🏻👏🏻

references:

--

--

--

Daily study logs about iOS development will be updated. Any recommendations or discussions are always welcome

Recommended from Medium

SwiftUI + Core ML + ARKit — Create an Object Detection iOS App

Camera on tripod

An Overview of Built-In SwiftUI Views

Phone

Handling Long Press Gesture [ CollectionView ]

Five Steps to Create a Chess App 2/5

iOS Combine: Call REST APIs

iOS Application Security Checklist

iOS or Android — Which Should You Develop First?

Create Contact App With SwiftUI and Xcode 12.2 — Part 1

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Doyeon

Doyeon

Hey! I am currently studying iOS by myself, and wanna be a cool app developer 🕶 please correct me if i’m wrong or if there’s any thing that you wanna share😊✌️

More from Medium

Swift questions and answers part -III

Dynamic lookup In Swift

Combine -101: Introduction to Basics & Terms

How capture lists work and how their behaviour differ for value and reference types in Swift