weak self

A few years ago, I joined a Swift project about Medical Health Care for the Japanese. I see some developers usually use some feature in Swift but don’t understand it. Also, I see a brother who doesn’t understand how Swift manages memory then he too scares about memory retain cycle matter. I decide to write the first article about how to resolve the memory retain cycle the right way. Let’s start

This article focus on how to prevent retain cycle when using closure the right way. Some use cases for other purposes don’t include in this article.

Retain cycle, The pain of iOS Developers

Automatic Reference Counting

ARC are 2 types of property is:

  • Increase reference count: strong
  • Don’t increase reference count: weak, unowned

Reference cycle(aka Retain cycle) occurs when two and greater objects reference to others that create a cycle. It will raise an issue is objects can not dealloc when don’t use anymore.

DispatchQueue.main.async {[weak self] in
	self?.reloadData()    
}

To prevent the reference cycle when use closure, developers use `[weak self]`. When using closure, Swift compile will retain all objects outside of closure to use later.

So, Use [weak self] in all cases of the developers that I did mention above will resolve the reference cycle. But we should use it in right place. So when we should use [weak self]

When we should use [weak self]

  • The closure was stored in a property of self.
  • The closure was stored in a property of other object. And, self stored that object.

So in some cases that objects and closure don’t create a cycle, [weak self] is not necessary. In runtime, weak is slower than strong.

Some cases we don’t need to use [weak self]

When calling self in the closure of DispatchQueue.main.async, then ref count of self increase by 1 and it will decrease after the closure dealloc.

DispatchQueue.main.async { in
	self.reloadData()    
}

When calling self in the closure of map function, ref count of self increase by one and will decrease when mapping is done.

let array = [1, 2, 3]
array.map { item in
	return self.mapItemIndex(item)
}

An example of right case we should use [weak self]

class A {
    var callback: ((String) -> Void)!
    
    func run() {
        self.callback("Demo value")
    }
}

class B {
    var instanceOfA: A = A()
    
    func setup() {
        instanceOfA.callback = {[weak self] value in
            self?.doPrint(val: value)
        }
    }
    
    func run() {
        instanceOfA.run()
    }
    
    func doPrint(val: String) {
        print("Value: \(val)")
    }
}

let b = B()
b.setup()
b.run()
If we don’t use [weak self], when variable b doesn’t use anymore. Then it has ref count is 1, instanceOfA is 1, and callback also 1. So 3 objects can be dealloc
In this case, we use [weak self]. When variable b is don’t use anymore. Ref count of it is 0. Variable instanceOfA will decrease to 0 and delloc. In that case, no object will be leak

Leave a Reply