Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is weak self needed in NSKeyValueObservation observe() closure?

I have:

private var statusLabel:   UILabel!
private var errorObserver: NSKeyValueObservation?

self.errorObserver = self.viewModel.observe(\.errorString)
{ [weak self] (viewModel, change) in
    self?.statusLabel.text = viewModel.errorString
}

Is [weak self] needed here?

like image 951
meaning-matters Avatar asked Mar 18 '18 11:03

meaning-matters


1 Answers

Short answer: Yes, you do need the [weak self]. Not having to explicitly remove the observer in deinit is a nice convenience that makes sure the Observation Center does not send notifications to de-initialized observers, but does not absolve you from having to manage memory for the closure.

Apple's Official Documents (The Swift Programming Language 4.0.3) states:

By default, a closure expression captures constants and variables from its surrounding scope with strong references to those values. You can use a capture list to explicitly control how values are captured in a closure.

A capture list is written as a comma-separated list of expressions surrounded by square brackets, before the list of parameters. If you use a capture list, you must also use the in keyword, even if you omit the parameter names, parameter types, and return type.

....

If the type of the expression’s value is a class, you can mark the expression in a capture list with weak or unowned to capture a weak or unowned reference to the expression’s value.

Excerpt From: Apple Inc. “The Swift Programming Language (Swift 4.0.3).” iBooks.

If you do not mark the expression self in a capture list, a strong reference cycle will be created. A strong reference cycle is created because classes are reference types, and closures are reference types. When the body of the closure refers to an instance of a class, a reference to the class instance is created by the closure. By default, this is a strong reference, unless you use a capture list to define a different type of reference. The official notes even say:

Swift requires you to write self.someProperty or self.someMethod() (rather than just someProperty or someMethod()) whenever you refer to a member of self within a closure. This helps you remember that it’s possible to capture self by accident.

In your case, you are referencing a label inside the body of the closure. The fact that you need to write

self?.statusLabel.text = viewModel.errorString

instead of simply

.statusLabel.text = viewModel.errorString

is a hint to you to use self inside a capture list.

like image 123
Jason Cross Avatar answered Nov 15 '22 17:11

Jason Cross