Deinit not calling - Cannot find why something is retaining (code provided)

Viewed 83

I have discovered that my UIViewcontroller is not calling deinit() under the following scenario. I am using this code extension to make my life easier by adding tap gesture recognizers.

https://gist.github.com/saoudrizwan/548aa90be174320fbaa6b3e71f01f6ae

I've used this code in one of my VCs, which I've stripped down to the barest minimum amount of code:

and in viewDidLoad() I did this:

// When the user taps on a label, have its related textbox automatically get the caret so they can type
// Add tapping so when you tap on a label it makes the corresponding textbox first responder
lblSubject.addTapGestureRecognizer {
 self.txtSubject.becomeFirstResponder()
}

It appears that the line:

self.txtSubject.becomeFirstResponder()

Is the problem - when I leave this line above in that closure, deinit() does not call in my VC. When I take the above line out or replace it with something like print("hello world") deinit() properly calls. txtSubject is @IBOutlet weak var txtSubject: UITextField!

I am not entirely sure what to do here. I read that when you trigger becomeFirstResponder() it's important you call resignFirstResponder(), but even if I don't tap the label (so as to not give becomeFirstResponder() a chance to even call) I still cannot hit deinit()

Any ideas where I can look further?

Thanks so much.

2 Answers

Change

self.txtSubject.becomeFirstResponder()

To

[unowned self] in self.txtSubject.becomeFirstResponder()

unowned is often feared as dangerous, but there is no danger here. If self ceases to exist, there will be nothing to tap and the code will never run.

This is a classic retain loop. The self. inside of the closure is there to remind you to think about this. I assume that self is retaining lblSubject, and (via a OBJC_ASSOCIATION_RETAIN associated key), lblSubject is retaining self because it's captured by this closure.

You don't really need self here, however. You just need txtSubject. So you can just capture that:

lblSubject.addTapGestureRecognizer { [txtSubject] in
    txtSubject.becomeFirstResponder()
}

Alternately, you can fall back to the giant weak self hammer (though this tends to be greatly over-used):

lblSubject.addTapGestureRecognizer { [weak self] in
    self?.txtSubject.becomeFirstResponder()
}

The best way to explore this kind of bug is with Xcode's Memory Graph.

It is also a good idea to review the Swift docs on Automatic Reference Counting.

Related