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

138 views Asked by At

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

There are 2 answers

3
matt On BEST ANSWER

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.

3
Rob Napier On

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.