Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Know when a weak var becomes nil in Swift?

Tags:

Let's say I have a weak var view: UIView? in my class Button {}. Is there any way to know when view loses its reference and becomes nil?

I tried using weak var view: UIView? {} (aka a computed property) in order to override set {}, but that didn't work because now it's a computed property and can't store a weak reference (how annoying!).

Edit:

@fqdn's answer didn't work with this code... Try it in an Xcode Playground

import UIKit  class Test {   weak var target: UIView? {     willSet {       if !newValue { println("target set to nil") }       else { println("target set to view") }     }   } }  class Button {   var view: UIView? = UIView() }  var t = Test() var b = Button() t.target = b.view b.view = nil // t.target's willSet should be fired here 

Your output console should display:

target set to view target set to nil 

My console displays

target set to view 

b.view is the strong reference for the UIView instance. t.target is the weak reference. Therefore, if b.view is set to nil, the UIView instance is deallocated and t.target will be equal to nil.

like image 818
aleclarson Avatar asked Jun 19 '14 22:06

aleclarson


1 Answers

If your button is holding a reference to another view, it should either be an owner of that view (i.e., it should hold a strong reference) or it should not care when that view goes away (i.e., its weak reference to it becomes nil.) There is no notification when weak references become nil, and that is by design.

In particular, Swift property observers are not called when weak references become nil, as the following code demonstrates:

class A : CustomStringConvertible {     var s: String?      init(s: String) {         self.s = s;         print("\(self) init")     }      deinit {         print("\(self) deinit")     }      var description: String {         get { return "[A s:\(s ?? "nil")]" }     } }  class B : CustomStringConvertible {     weak var a:A? {         willSet {             print("\(self) willSet a")         }         didSet {             print("\(self) didSet a")         }     }      init(a: A?) {         self.a = a         print("\(self) init")     }      deinit {         print("\(self) deinit")     }      var description: String {         get { return "[B a:\(a == nil ? "nil" : String(describing: a!))]" }     } }  func work() {     var a: A? = A(s: "Hello")     var b = B(a: a)     print("\(b)")     a = nil     print("\(b)")     b.a = A(s: "Goodbye") }  work() 

When work() is called, the console gives the following output:

[A s:Hello] init [B a:[A s:Hello]] init [B a:[A s:Hello]] [A s:Hello] deinit [B a:nil] [A s:Goodbye] init [B a:nil] willSet a [B a:[A s:Goodbye]] didSet a [A s:Goodbye] deinit [B a:nil] deinit 

Notice that in neither case of the instance of A deallocating and its weak reference in the instance of B becoming nil are the property observers called. Only in the direct case of assignment to B.a are they called.

like image 129
Wolf McNally Avatar answered Sep 23 '22 12:09

Wolf McNally