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.
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
I have UITabBarController and in one of the UIViewController there I scroll UICollectionView each 5 second using Timer. Here is short code how I do it:
override func viewDidLoad() {
super.viewDidLoad()
configureTimer()
}
private func configureTimer() {
slideTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(scrollCollectionView), userInfo: nil, repeats: true)
}
#objc func scrollCollectionView() {
collectionView.scrollToItem(at: someIndexPath, at: .centeredHorizontally, animated: true)
}
It perfectly works. But I think it has a big issue. Of course, I can open another screen from this UIViewController (for example, I can tap to another tab or push another UIViewController). It means, my UIViewController's view, containing UICollectionView, disappears. In another words, viewDidDissapear will be called. But my timer still exists and I am having strong reference to it, possibly, there is retain cycle. It keeps working and each 5 second scrollCollectionView method is called even my view dissapeared. I don't know how, but iOS somehow handles it. In other words, it can modify view even it is not visible. How is that possible and is it good practice? Of course, I can invalidate my timer in viewDidDissapear and start it in viewDidAppear. But I don't want to loose my timer value and don't want to start it from zero again. Or may be it is ok to invalidate my timer in deinit?
My question covers pretty common situation. For instance, if I make network request and open another UIViewController. After that request finished, I should modify UI, but now am on another screen. Is it ok to allow iOS to modify UI even it is not visible?
A couple of thoughts:
If the timer is updating the UI at some interval, you definitely should start it in viewDidAppear and stop it in viewDidDisappear. There’s no point in wasting resources updating a view that is not visible.
By doing this, you can solve your strong reference cycle, too.
In terms of “losing” your timer value and starting at zero, we generally would just save the time you’re counting from or to and calculate the necessary value when restarting the timer later.
We do this, anyway, because you really shouldn’t be using timers to increment values because you’re technically not assured that they’ll be called with the frequency you expect.
All of that said, I don’t know what timer “value” you’re worried about losing in this example.
But definitely don’t waste time updating a UI that is no longer visible. It’s not scalable and blurs the distinction between the model (what you’re counting to or from) from the UI (the update that happens every five seconds).
I am using an show segue and an unwind segue to navigate between two iOS viewControllers, VC1 and VC2. In the viewDidLoad() of VC2 I make VC2 an observer. Here is my code:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "buzzer updated"), object: nil, queue: OperationQueue.main) { _ in
print("set beeperViewImage")
}
}
Every time I use the unwind segue to go from VC2 back to VC1 the addObserver() gets called an additional time, e.g., on the fourth return segue addObserver is called 4 time; on the fifth segue, five times, etc. This behavior happens even when the app is sent to the background and recalled. It remembers how many segues happened in the previous session and picks up the count from there.
I have no problems with multiple calls in VC1, which is the initial VC.
I have tried to set VC2 to nil after unwind segueing.
Looking forward to any guidance.
This is undoubtedly a case where your view controllers are not getting released. Perhaps you have a strong reference cycle.
For example, consider this innocuous example:
extension Notification.Name {
static let buzzer = Notification.Name(rawValue: Bundle.main.bundleIdentifier! + ".buzzer")
}
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(forName: .buzzer, object: nil, queue: .main) { _ in
self.foo()
}
}
func foo() { ... }
}
If I then enter and leave this view controller three times and then click on the “debug memory graph” button, I will see the following:
I can see three instances of my second view controller in the panel on the left and if they were properly deallocated, they wouldn’t show up there. And when I click on any one of them in that panel, I can see a visual graph of what still has a strong reference to the view controller in question.
In this case, because I turned on the “Malloc Stack” feature under “Product” » “Scheme” » “Edit Scheme...” » “Run” » “Diagnostics” » “Logging”, I can see the stack trace in the right most panel, and can even click on the arrow button and be taken to the offending code:
In this particular example, the problem was that I (deliberately, for illustrative purposes) introduced a persistent strong reference where the Notification Center is maintaining a strong reference to self, imposed by the closure of the observer. This is easily fixed, by using the [weak self] pattern in that closure:
NotificationCenter.default.addObserver(forName: .buzzer, object: nil, queue: .main) { [weak self] _ in
self?.foo()
}
Now, I don’t know if this is the source of the strong reference cycle in your case because the code in your snippet doesn’t actually reference self. Perhaps you simplified your code snippet when you shared it with us. Maybe you have something completely else that is keeping reference to your view controllers.
But by using this “Debug Memory Graph” button, you can not only (a) confirm that there really are multiple instances of your relevant view controller in memory; but also (b) identify what established that strong reference. From there, you can diagnose what is the root cause of the problem. But the code in your question is insufficient to produce this problem, but I suspect a strong reference cycle somewhere.
Thank you all for your comments on my problem. Based on them, I started searching for what might be holding on to my VC2. Turns out that a call to read a bluetooth radio link in my VC2 viewWillAppear() was the culprit but I don't understand why:
self.radio?.peripheral?.readValue(for: (self.radio?.buzzerChar)!)
Everything works fine after removing the above line of code. Thanks again for pointing out in which direction to search.
I am relatively new to iOS development with Swift (I actually have 3 years of experience with Android development with Java, trying to learn a new technology). I am creating an app that requires the usage of a library known as SearchTextField:
https://github.com/apasccon/SearchTextField
In a shellnut, it's a UITextField subclass that has a dropdown suggestions/autocomplete functionality.
Below is the ViewController that uses it...
#IBOutlet var homeAddressTextField: SearchTextField!
#IBOutlet var workAddressTextField: SearchTextField!
override func viewDidLoad() {
super.viewDidLoad()
homeAddressTextField.delegate = self
workAddressTextField.delegate = self
homeAddressTextField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
workAddressTextField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
//vvvvvvvvv EXC_BAD_ACCESS CODE 2 THROWN BELOW vvvvvvvv
homeAddressTextField.filterStrings(["foo","bar"])
}
homeAddressTextField should be instantiated, otherwise any reference to it above should throw the same exception. When breakpointing into the problematic line, homeAddressTextField is NOT nil, and correctly shows that it is an instance of SearchTextField.
I have tried many things to fix or at least find the source of the error. As you can tell, I used a strong var instead of weak var for the Outlet.
I have tried using zombies to track any attempt to access a deallocated memory block, yet the zombie Instruments came up with no zombies accessed.
If it is worth noting, the error disappears as soon as the problematic line containing filterStrings() is removed. Any help is appreciated!
It seems bug in library, could you please check here
SearchTextField Issue
It is in still open issues at their repository.
Kindly watch issues in repository, if you try to use someone readymade code.
Are you sure you've attached your IBOutlet in interface builder?
Try putting a breakpoint on the line that's crashing. That will stop it right before executing that line. Then, in the console (command+shift+y) you'll see a line that says "lldb" - put your cursor there and type po homeAddressTextField and see if it returns a value, or nil. If nil, then the IBOutlet is not set properly, which would cause bad access.
Additionally, if it is indeed nil, you'll want to make sure that the subclass and module are both set within interface builder on the SearchTextField, as well as making sure to set the outlet itself. You can also try filtering these strings in the viewDidAppear() method just to see if it is indeed an issue with the reference to the SearchTextField.
Edit: I've looked through the code a bit of the repo. You might not want to set the datasource and delegate properties, as the SearchTextField has a datasource and delegate of its own. You simply need to set the filterable strings as you are on the last line. So try removing the calls to make the view controller the datasource/delegate.
Clicking the textField and then adding class name = SearchTextField.swift worked for me.
Everyone tells me "Use super.viewDidLoad() because it's just like that" or "I've been doing it always like that, so keep it", "It's wrong if you don't call super", etc.
override func viewDidLoad() {
super.viewDidLoad()
// other stuff goes here
}
I've only found a few topics about Objective-C cases and they were not so enlightening, but I'm developing in Swift 3, so can any expert give me a good detailed explanation on this?
Is it a case of just good practice or are there any hidden effects?
Usually it's a good idea to call super for all functions you override that don't have a return value.
You don't know the implementation of viewDidLoad. UIViewController could be doing some important setup stuff there and not calling it would not give it the chance to run it's own viewDidLoad code.
Same thing goes when inheriting from a UIViewController subclass.
Even if calling super.viewDidLoad doesn't do anything, always calling it is a good habit to get into. If you get into the habit of not calling it, you might forget to call it when it's needed. For example when subclassing a ViewController that depends on it from a 3rd party framework or from your own code base.
Take this contrived example:
class PrintingViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("view has loaded")
}
}
class UserViewController: PrintingViewController {
override func viewDidLoad() {
super.viewDidLoad()
// do view setup here
}
}
Not calling viewDidLoad here would never give PrintingViewController a chance to run its own viewDidLoad code
If you don't want to do anything in viewDidLoad just don't implement it. The super method will be called anyway.
I have a secret, when I worked at Apple I read the source code for UIKit, partly to answer questions I had like this, viewDidLoad is empty in all the UI*ViewController classes.
Naturally I am not there anymore, they may have changed this.
I think calling super.viewDidLoad() is, first of all, a good practice.
The usual thing in iOS is to do all of your subclass setups after the superclass has completed the setup that it needs to do (initializing properties, laying things out, etc.). If you don't give the superclass a chance to handle all of its setups before you start changing things around, it's possible you'll encounter some strange bugs and behavior.
We can draw a parallel with Class Initialization: "A designated initializer must delegate up to a superclass initializer before assigning a value to an inherited property." We're doing this to be sure that all superclass properties have a value and based on that fact we could safely use them through inheritance in our subclass.
Rule of thumb:
When initializing/setting up, run the superclass' implementations first.
When tearing down/cleaning up, run the superclass' implementation last.
Assuming that viewDidLoad() is some sort of initialization we should call super.viewDidLoad() first to correctly set things up in the superclass.
If we check the implementation of viewDidLoad() in UIViewController base class, we can see that it's empty. So maybe the only one reason to calling super.viewDidLoad() from you child class is a good coding style :-) Lets follow it!
That depends on the implementation of viewDidLoad in the class UIViewController (from which all view controllers inherit). If it's empty than calling super.viewDidLoad() won't do much. However if it has some functionality regarding the view controller then you certainly would want to use it.
Since it's not in your hands regarding the implementation of a UIViewController you should always call this method
When inheriting directly from UIViewController, call super when its documentation tells you to.
For example, viewWillAppear(Bool) says, "If you override this method, you must call super at some point in your implementation," whereas viewDidLoad() does not.
If you are not inheriting directly from UIViewController and the class you are inheriting from does not have reliable documentation, or may silently introduce a breaking change requiring that super be called, then always call super.
if your class is been inherited from UIViewController directly then there is no need to invoke super.viewDidLoad. This definately make your code look bit consice but usually iOS community suggest to call it anyway.
if your class is been inherited from custom UIViewController which indeed has some functionality which your class can leverage then invoke super.viewDidLoad.
SOLVED BELOW
I'm reading this article from raywenderlich blog:
http://www.raywenderlich.com/23037/how-to-use-instruments-in-xcode
to learn about instruments and figure out if I´m doing something wrong in some old projects.
I've seen that in one particular point of my code, when I'm showing a modal view that eventually is closed, the memory allocated remains there. As you can see in the following image.
The execution have 4 marks generated.
Between the 2n and the 3t mark, the view is showed, as you can see, new memory is allocated.
But between the 3t and the 4th, I've called dismissViewController, and the view no longer remains. But the memory remains allocated.
All the properties, are created as strong (probably no the best approach):
And I´ve an NSTimer, that is initialized in viewDidLoad method, and set to nil at viewWillDisappear:
[self.secondTimer invalidate];
self.secondTimer = nil;
So, do you have any idea about what's happening? From what I know, even the properties are declared as strong, when the UIViewController is released, all of them are going to be released to.
EDIT
Thanks to all, with the information I provided, wasn't enough.
As you can see, QRViewController inherits from BaseViewController.
This controller had a delegate defined as strong storage, terrible.
So that's all.
In the view controller hierarchy, self.view holds ALL his subviews with strong, so everything under self.view (Probably all your IBOutlet properties) can switch to weak. That probably won't solve the problem though.
What might help you is the fact that any block you have holds every single object used in that block as a strong, to make sure the block can run it's code at the time being. If nothing holds that block (like a animationWithDuration:) than no worries. But if you have any block that an object is holding (Like and object's "completion-block" or any other creative use of blocks), everything within that block will be strong, and there's a chance you create a retain cycle that way. For example: the presenting view controller is calling the presented view controller with a completion block, and in that block you use self. Now presented VC is holding a block to perform on dismiss, and the block holds the presenting VC. When dismissed you will end up with a VC that holds a block that holds a VC that holds the presented VC....
A simple solution would be to give the block a weak version of self and only when the block executes, make it strong for the time of running the block (To avoid dealloc while running the block):
__weak myViewController *weakself = self;
[self.someObject setBlockHandler:^(BOOL foo){
myViewController *strongself = weakself;
if (strongself) {
// Do whatever...
}
}];
It's difficult to pinpoint precisely the problem, but usually when things like this happen to me, it winds up being one (or a few) "root" culprits -- you find that one, clear it up, and then lots of others clear up too. So one strategy you can try is to sift through the Instruments data looking for any sort of "hierarchy" (think about how your app is structured and how the objects relate to each other) and look for objects closer to the base, then cross-reference against your code to see if they might have a retain cycle or some other such issue.
One immediate change I would make would be to change your IBOutlet declarations from strong to weak. For the most part, IBOutlet properties should be weak, for objects that are within a hierarchy. So if say you've got some UILabel within your xib's main view, that label should be weakly-retained so as to avoid a retain cycle. But if say that UILabel is standing alone as a root item within the xib, then it would need a strong reference. I'm going to guess most (if not all) of your IBOutlets there are within a hierarchy, so make them weak and try again. It may not solve all the leaks, but see if it makes any difference.
This is called Abandoned Memory, check this link.
TIP:
If you are navigating between view controllers, and you perform the navigation inside a closure, you should use a weak or unowned version of self, example:
//Swift 2.1
//Performing naivgation on the main thread for responsiveness:
dispatch_async(dispatch_get_main_queue(), {[weak self] () -> Void in
if let weakSelf = self{
weakSelf.performSegueWithIdentifier("myOtherView", sender: weakSelf)
}
})
Also, when dismissing the view controller is the same:
dispatch_async(dispatch_get_main_queue(), {[weak self] () -> Void in
if let weakSelf = self{
weakSelf.dismissViewControllerAnimated(true, completion: nil)
}
})
The posted link above shows a practical example on how to catch abanodend memory using Xcode Instruments.