The Strong, the Weak and the Unowned

It’s the Fall of 2011, Adele’s “Rolling in the Deep” is blasting from little white headphones everywhere. It was a simpler time, except in the area of iOS memory management. Something was about to change. With iOS5 came the wonder of Automatic Reference Counting (ARC). App memory management became a lot easier. However, there are still a few scenarios which, if left unchecked, could have you “Rolling in the S***”.

ARC Me

Simply put, every time an instance of a class is made a chuck of memory is allocated to save information about that instance. Automatic Reference Counting keeps track of how many variable, constants and properties are referenced in each class instance. When there are zero references to the class instance it gets freed up from memory.

ARC only applies to reference types: aka classes and closures, aka memory allocation that’s stored on the heap. This means that ARC doesn’t apply to value types (structs and enums, which have their memory allocated on the stack). Note: it doesn’t make any sense to specify a kind of memory reference (strong, weak or unowned) for a variable within a struct.

One way to avoid any reference counting shenanigans may be to model your data using structs instead of clasess. +108 SwiftySmug points for using a struct. +928 SwiftySmug points for implementing a protocol in that struct!

Let’s go over those three kinds of memory relationships: strong, weak and unowned (the scary one!).

Strong

By default every constant, variable or property you create in a class has a strong reference to the class (even though there’s no strong keyword in-front of it). The means that a reference to the class instance will be stored in memory as long as there’s at least one active reference to it.

In Objective-C land you would specifically declared these properties with the strong keyword.

This sort of relationship is great when there’s a hierarchy of objects is linear, going from parent to child. For example, a UIViewController containing a UIImageView, containing a UIImage. The view controller contains a reference to the UIImageView. Note that inside the instance of UIImageView there isn’t a reference (a variable or constant) with the view controller assigned to it (right!?). The relationship goes one way.

The deep trouble starts if you create a strong reference cycle between classes.

class Guitarist {
    let name: String
    
    init(name: String) {
        self.name = name
        print ("Memory is allocated for \(name)")
    }
    var favoriteGuitar: Guitar?
    deinit {
        print("Guitarist \(name) is being deinitialized")
    }
}

class Guitar {
    let make: String
    let model: String
    
    init(make: String, model: String) {
        self.make = make
        self.model = model
        print ("Memory is allocated for \(make) \(model)")
    }
    var knownPlayer: Guitarist?
    deinit {
        print("Guitar \(make) is being deinitialized")
    }
}

// define two variables with optional type nil
var guitaristReference: Guitarist?
var guitarReference: Guitar?

// initialize these varaibles
guitaristReference = Guitarist(name: "JimiHendrix")
guitarReference = Guitar(make: "Fender", model: "Stratocaster")

// ! is used to unwrap and access the instances stored the variables
guitaristReference!.favoriteGuitar = guitarReference
guitarReference!.knownPlayer = guitaristReference

There’s now a strong reference between the guitarist instance to the guitar class and also a strong reference from the guitar instance back to the guitarist. Let’s try to deinitialize these classes:

guitaristReference = nil
guitarReference = nil

What what what! In both cases the print statement in the deinit method is never called. Adele is sad.

Weak

The strong reference cycle in the problem above can be broken by making the reference to Guitarist in the Guitar class a weak reference. This is achieved simply by adding the weak keyword before the var. Our Guitar class now looks like this:

class Guitar {
    let make: String
    let model: String
    
    init(make: String, model: String) {
        self.make = make
        self.model = model
        print ("Memory is allocated for \(make) \(model)")
    }
    weak var knownPlayer: Guitarist?
    deinit {
        print("Guitar \(make) is being deinitialized")
    }
}

The guitarist class has a strong reference to the Guitar class and the Guitar class has a weak reference back. If you run this in Xcode you’ll see that when the instances of the classes are set to nil both deinit print statement are executed and the memory is freed up.

Unowned V Weak

Weak and unowned declarations are similar in that both keywords are used to break this strong reference cycle behavior. i.e. neither of them increase the retain count of the class they are referencing. The difference is:

  • Weak references are used when the variable or property could not have a value at some point in its life (it could be nil).
  • Unowned references must always have a value. Because of this they must be used with caution to avoid crashes! Much like as with implicitly unwrapped optionals

In the example above the reference to Guitarist within the Guitar class is declared as weak because hypothetically in this data model we could have a make of guitar that doesn’t have any known guitarist associated with it.  To handle this weak references must be declared as optional!

Here’s a similar scenario but where an unowned reference is more appropriate:

class GuitarBuyer {
    let name: String
    var customShopGuitar: CustomGuitar?
    
    init(name: String) {
        self.name = name
        print ("Memory is allocated for \(name)")
    }
    deinit {
        print("Guitar buyer \(name) is being deinitialized")
    }
}

class CustomGuitar {
    let orderNumber: Int
    unowned let guitarBuyer: GuitarBuyer
    
    init(orderNumber: Int, guitarBuyer: GuitarBuyer) {
        self.orderNumber = orderNumber
        self.guitarBuyer = guitarBuyer
    }
    deinit {
        print("Guitar order \(orderNumber) is being deinitialized")
    }
}

// implementation

var guitarBuyer: GuitarBuyer?

guitarBuyer = GuitarBuyer(name: "Eric Johnson")
guitarBuyer!.customShopGuitar = CustomGuitar(orderNumber: 198234, guitarBuyer: guitarBuyer!)

// deinit GuitarBuyer instance

guitarBuyer = nil   // both deinit print statements for GuitarBuyer and CustomGuitar are called

Here we have two classes: GuitarBuyer and CustomGuitar. In this case our class for the guitar object must have a guitarist (a buyer) associated with it. Because of this whenever an instance of a CustomGuitar is created a GuitarBuyer must be passed into the initializer.

Each hold a reference to each other, so there’s the potential for a strong reference cycle. In this case we’ll break the reference cycle using the unowned keyword because within an instance of CustomGuitar we are guaranteed that the guitarBuyer variable will never be nil. You can check that the reference cycle is being broken because when guitarBuyer is set to nil both deinit print statements are called within the GuitarBuyer and CustomGuitar classes.

Conclusion

Thankfully Automatic Reference Counting has abstracted away a lot of the hard labor of memory management.  Aside from  using the strong, weak and unowned keywords before instance properties / variables one area where you have to be particularly conscious of memory references is inside closures. This will be a topic for a future blog!

You can download the playground on GitHub here 

If you’ve found the post helpful please share with the links below!

For the latest updates + blog posts follow me on twitter here.

Leave a Reply