Search code examples
iosswiftswift3

Why would I ever use unowned self?


The following pattern happens a lot in iOS apps:

class MyViewController: UIViewController {
    let myModel = MyModel()

    override func viewDidLoad() {
        super.viewDidLoad()
        myModel.foo() { [***] in
            // use self here
        }
    }
}

class MyModel {
    public func foo(complete: () -> Void) {
        // do something
        complete()
    }
}

The consensus is to use either [unowned self] or [weak self] in place of the [***], unowned when you can guarantee that self will not be nil at the time of completion and weak when you're not sure the reference will still be valid.
What I don't understand is why I would ever risk using unowned, maybe I'm sure that right now the reference will never be nil, but that can change in the future. I could also have overlooked an edge-case, mistakes happen. I could just as easily always use weak, and put a guard at the top of the closure to be able to use self without a ! or ?.
What is the use of unowned? Is it faster than weak+guard? Is it syntactic sugar? It seems to go against Swift's philosophy of protecting the developer against common mistakes that can cause crashes.


Solution

  • unowned has a marginal performance advantage over weak because the runtime doesn't have to keep track of the reference to turn it into nil when the object goes away.

    In respect of retain cycles (well, strong reference cycles), neither weak nor unowned creates a strong reference (in pre ARC terms, neither increments the retain count) so there is no danger of a reference cycle, in fact, that is why you need to specify weak or unowned for self in closures.

    Also, with unowned you can use the reference as a non optional, so you don't have to put any code in the closure to unwrap it.

    I always use weak unless there is a really good performance reason not to.

    NB in your code, I do not think either is necessary because the closure is not escaping i.e. The reference to it taken in the function call foo does not persist after the end of foo's scope.