Liam SY Kim
by Liam SY Kim
3 min read

Categories

We have heard many times about memory leak issues, but it is hard to understand 100%.

Usually in swift, retain cycles occur memory leak often. Therefore, understading ARC and retain cycles will show the way how to manage memory.

ARC is abbreviation of Automatic Reference Counting, and MRC is abbreviation of Manual Refetence Counting.

Okay then, What is the ‘Reference Counting’?

This is the reason what makes me difficult to understand this concept at first.

Object internally stores reference count.

If the object is referenced by variable, then we increase +1 reference count.

ARC

From the above __‘Reference Counting’ __
explaination, you could predict what ARC is.

Easily say, Object automatically deinitailize after reference removed.

In detail, when the reference is removed by variable, Reference count of object automatically will be -1 decreased.

If reference count is zero, then the object automatically would be deitialized as well.

Swift supports ARC, so we don’t need to manually count the number of object reference.

This can be known by code.

class FruitClass {
    init() {
        print("init")
    }
    
    deinit{
        print("deinit")
    }
}

var fruitClass : FruitClass? = FruitClass()
fruitClass = nil

I use fruit example as usual, it does not have any meaning about fruit.

Because ARC supports automatic denitializing, you can see the result as below.

init
deinit

MRC

I will not explain it much, but you can imagine it does not support automatic reference count.

When we used obj-c, we should count reference manually.

You should manually make and count reference on obj-c by using alloc, new, cioy, mutalbeCopy, retain.. etc.

Because swift supports ARC, We do not need to code with MRC, but it is good to know the concepts above to understand ARC well.

Retain Cycle

Since ARC supports everything, it looks like we do not need to care about memory management.

However, there is a exeption to deal with. Yes, It is retain cycle.

Some cases like delegate pattern, we should pair two object as reference.

At first in this example, each instance object has 2 references.

If both fruitClass1 and fruitClass2 remove their reference about instance object,

Each object remain 1 reference count after removed reference by variable.

In this case, Objects are remained in memory.



class FruitRetainClass{
    var fruitRetainClass: FruitRetainClass? = nil
    init(){
        print("init")
    }
    deinit{
        print("deinit")
    }
}

var fruitRetainClass1: FruitRetainClass? = FruitRetainClass()
var fruitRetainClass2: FruitRetainClass? = FruitRetainClass()

fruitRetainClass1?.fruitRetainClass = fruitRetainClass2
fruitRetainClass2?.fruitRetainClass = fruitRetainClass1

fruitRetainClass1 = nil
fruitRetainClass2 = nil

This can be checked by code as well.

The result is

init
init

We cannot see the result of deinit.

Weak

Reference which we used until now, is strong reference. strong reference references object and counts reference count.

However we need additional reference system for solving retain cycles issue. Yes, It is weak reference.

Weak reference references object, but it does not count reference count.

Okay, then how can weak reference solve retain cycles issue?

As I explained, if the reference count of object is zero in ARC, and then the object is automatically deinitailized.

You can predict the result of retain cycles based on above fact.

Because weak reference never counted, FruitClass1 instance and FruitClass2 instance’s reference count is originally 1.

After instance object reference is removed by fruitClass1 and fruitClass2, both FruitClass1 instance object and FruitClass2 instance object’s reference count is zero.

As a result, objects are deititailized.

As same way, you can also prevent retain cycles by only making a reference as weak.

This result can be shown by code example.

FruitRetainClass1 bind to FruitRetainClass2 as weak, and FruitRetainClass2 bind to FruitRetainClass1 as strong below.

class FruitRetainClass1{
    weak var fruitRetainClass: FruitRetainClass2? = nil
    init(){
        print("init")
    }
    deinit{
        print("deinit")
    }
}

class FruitRetainClass2{
    var fruitRetainClass: FruitRetainClass1? = nil
    init(){
        print("init")
    }
    deinit{
        print("deinit")
    }
}

var fruitRetainClass1: FruitRetainClass1? = FruitRetainClass1()
var fruitRetainClass2: FruitRetainClass2? = FruitRetainClass2()

fruitRetainClass1?.fruitRetainClass = fruitRetainClass2
fruitRetainClass2?.fruitRetainClass = fruitRetainClass1

fruitRetainClass1 = nil
fruitRetainClass2 = nil

the result is

init
init
deinit
deinit

Therefore, making ‘weak’ the reference of retain cycle, makes problem solved.

Unowned

It has same function as weak, but additionally it can not be nil. This means you can use unowned only if it does not access removed memory space.

Usually, Unowned is not recommanded.

Conclusion

I have searched many times about ARC, but It is difficult to fully understand.

I feel like, “Okay, automatically count reference.. so what?” until understanding with these picture.

Drawing the image of reference relations would be help you to understand concepts well.

Reference