Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Zeroing weak variable and deinit, which one happens first?

Which one happens first?

  • Zeroing (nilling) weak variable.
  • deinit
like image 771
eonil Avatar asked Dec 14 '22 18:12

eonil


2 Answers

Without looking at the documentation, nor implementation...

Only one order makes sense: nilling has to come first.

If deinitialization would start before nilling weak references ARC would suffer the good old resurrection problem (retaining an object that is in the process of being deallocated). That is not the case.

Here's my mental model of object destruction (again, not from the documentation, this can differ from real world):

  1. last strong reference to an object goes away
  2. retain count goes to zero (logically)
  3. object is internally flagged for destruction, disabling any more new references
  4. all weak references are nilled
  5. unowned reference count is checked and traps if non-zero
  6. deinit chain is called, possibly calling through to objc base classes dealloc
  7. strong references to properties and ivars go away
  8. objc side effects of dtor happen (associated objects, c++ destruction, ...)
  9. memory is reclaimed

Step 1 through 4 happen atomically with regards to other threads potentially taking new strong references to the object.

like image 74
Nikolai Ruhe Avatar answered Jan 17 '23 18:01

Nikolai Ruhe


Zeroing weak variable happens first. deinit happens later. At least in current implementation (Xcode 6.1, Swift 1.1) This is a result of observation of specific implementation, and I don't know how it is actually defined by the authors... If you have explicit source, please comment or answer.

There's also a related discussion in ADC forum.

Test code Avoid Playground when testing this to get correct lifecycle behaviour.

class AAA {
    func test() {
    }
}

var         a1  =   nil as AAA?
weak var    a2  =   nil as AAA?

class BBB: AAA {
    var data    =   "Here be dragons."
    override func test() {
        println("test() called and a2 is now \(a2).")
    }
    deinit {
        println("deinit called and a2 is now \(a2).")
    }
}

a1  =   BBB()
a2  =   a1
a2!.test()

a1  =   nil

Result:

test() called and a2 is now Optional(weak_deinit_order_comparison.BBB).
deinit called and a2 is now nil.

Then, the weak variable becomes nil before the deinit to be called.

Update

This pre-nilling is applied equally to unowned objects. Unowned object will become inaccessible at the point of deist just like weak, and trial to access unowned object at the deinit will crash the app.

Update 2

If you assign self to a weak var variable in deinit, it will become nil immediately. (Xcode Version 6.3.2 (6D2105))

class Foo {
    init() {

    }
    deinit {
        var         a   =   self
        weak var    b   =   self
        unowned var c   =   self
        let         d   =   Unmanaged.passUnretained(self)

        println(a)  // prints `Foo`.
        println(b)  // prints `nil`.
//      println(c)  // crashes.
        println(d.takeUnretainedValue()) // prints `Foo`.
    }
}


var f   =   Foo() as Foo?
f       =   nil
like image 30
eonil Avatar answered Jan 17 '23 18:01

eonil