0

I have a parent class and a child class. The parent has a strong reference to the child, and the child has an unowned reference to the parent. During deinit of the parent, I want the child to do some cleaning up, and that involves calling the parent:

class ViewController: UIViewController
{
    override func viewDidLoad()
    {
        super.viewDidLoad()

        let parent = Parent()
    }
}

class Parent : NSObject
{
    override init()
    {
        super.init()

        child.doStuff()
    }

    deinit
    {
        child.doStuff()
    }

    lazy private var child : Child = Child(parent : self)
}

class Child : NSObject
{
    init(parent : NSObject)
    {
        self.parent = parent
    }

    func doStuff()
    {
        println(self.parent)
    }

    deinit
    {

    }

    private unowned var parent : NSObject
}

Unfortunately, calling doStuff() during deinit of the parent causes a crash, as it uses self.parent:

libswiftCore.dylib`_swift_abortRetainUnowned:
    0x111e91740 <+0>:  leaq   0x1e6cd(%rip), %rax       ; "attempted to retain deallocated object"
    0x111e91747 <+7>:  movq   %rax, 0x58612(%rip)       ; gCRAnnotations + 8
    0x111e9174e <+14>: int3   
->  0x111e9174f <+15>: nop    

As far as I understand, the parent should still exist because deinit of the parent has not yet completed. Yet this error seems to suggest that the child can no longer access its unowned reference to the parent.

Can anyone shed any light on this?

JimmyB
  • 326
  • 3
  • 12

3 Answers3

1

In this case unowned(unsafe) will do it. But I would personally not use unowned(unsafe) for anything but bridging objective-c code.

If possible I would try to avoid the need to call child.doStuff() from the deinit() if possible. I have had similar cases where I simply added a .unload() method that my own code was responsible for calling when time needed. In the above example the ViewController could accept this responsibility.

And I guess the utopian solution would be, to find a way where the objects are not so intertwined by design, when/if possible of course.

Example of unload() scenario: (I tested it in the terminal repl, therefore no UIKit)

import Foundation

class ViewController {
  let parent = Parent()

  deinit {
    parent.unload()
  }
}

class Parent {

  init() {
    child.doStuff()
  }

  func unload() {
    // Code used to be in deinit
    child.doStuff()
  }

  lazy private var child : Child = Child(parent : self)
}

class Child {

  init(parent : Parent) {
    self.parent = parent
  }

  func doStuff() {
    println(self.parent)
  }

  private unowned var parent : Parent
}

var vc:ViewController? = ViewController()
vc = nil
Mikael Hellman
  • 2,664
  • 14
  • 22
1

How about the parent passes itself as an argument to the child's method that needs it:

class Parent
{
    deinit
    {
        child.doStuff(self)
    }
}

class Child
{
    func doStuff(parent)
    {
        println(parent)
    }
}
newacct
  • 119,665
  • 29
  • 163
  • 224
0

Using unowned(unsafe) fixes this issue. It seems quite dangerous in general, but in this context it's OK because the parent is guaranteed to exist while the child exists.

JimmyB
  • 326
  • 3
  • 12