I have a view contoller (for log in) that defines deinit method to remove observers. When user taps on Register button, the controller calls performSegueWithIdentifier to load registration view controller. I defined deinit method to remove all observers. However, that method is not being called. I read it somewhere that it is because the viewcontroller is not being destroyed and the pointer to it is hold somewhere. Could anyone explain the reason behind this?
Thanks
EDIT : Although I agree that the link provided in the comment section holds the same question, there is no answer or clear answer to that. The highest upvoted reply suggested to define something inside deinit instead of leaving it blank. That does not answer the explination I'm looking for. For that reason, I'm keeping this question until somebody can point out my understanding is incorrect.And also, I do think that Matt explains it succinctly and clearly.
The usual reason for failure to trigger deinit when expected is that you have a retain cycle that prevents your view controller from going out of existence.
(Sometimes the reason is that your expectation that the view controller would be destroyed under the circumstances is incorrect. But assuming it is correct, a retain cycle is the reason.)
You mentioned removing all observers. What kind of observers? If we're talking about NSNotification, that is often how you get a retain cycle. The notification center retains the observer until you unregister it. You thus cannot get deinit until after the observer has been removed. Therefore, you cannot remove the observer in deinit.
Related
When a request is sent from a viewcontroller having a completion handler in place, but before the request can be received by the device, we go back to the previous view. What will happen to that completion handler block?
When are objects deallocated from memory?
Is it when the related object is moved away from the screen? Wrong!
Or is it When the related object is removed from memory? Right!
All objects are removed from memory when 'there is nothing left to strongly point to them'. This could happen when the view goes offscreen. See AutomaticReferenceCounting from docs.
Closures or completionHandlers, point to the instance. Hence they will hold the instance in memory. The instance itself will also point to the completionHandler. So both would be waiting for the other to be removed from memory. As if two people each say I would like you exit the house first. Ending result is that that none of them leave.
You need to avoid that from happening. As Sh_Khan said you do that with [weak self]. For more on why we do that. See Reference to property in closure requires explicit 'self.' to make capture semantics explicit
This case is the reason why we need to put [Weak self] in completion , to avoid strong references to that vc and so to be deallocated , if you skipped it , then what you did in completion will run but you'll not see anything as the vc's view is hidden after the pop while this instance is still in memory and will cause memory drains by time
The [UIViewController viewDidLoad] method is called by the system after loading the associated view (obviously?). A common belief -- which I share -- is that viewDidLoad should not be called directly †. However, I can't find this guidance in the documentation, nor anything else from Apple. Does it exist?
For comparison, the loadView documentation says
You should never call this method directly.
† Excepting [super viewDidLoad] in an overridden method.
I don't think there's any rule that forbids it. I don't mean "there's a rule we all know, it's just not written down." I mean "I don't think there's any rule that forbids it." You are, as best I am aware, free to call viewDidLoad whenever appropriate (including its super).
That doesn't mean you should call it. But then you almost certainly should never call OSCompareAndSwap either. In both cases, if you had a good reason and knew what you were doing, it could be appropriate to call. But it's unlikely to come up.
While I can't think of a time I've had to call viewDidLoad directly, I have had to manually call viewWillAppear and viewDidDisappear to manage view lifecycle in a custom container view controller. There's nothing that forbids calling view lifecycle methods if that's what you mean.
That said, it would be bad practice to call a view lifecycle method if you didn't mean "the view has transitioned into this state." And since it's pretty hard to get into a situation where you have loaded the view, but viewDidLoad won't be called, it's hard to imagine many cases where it would be useful. And you shouldn't call a method uselessly. So that's the only rule at play here as far as I'm aware.
It is not mentioned explicitly but if you would call it, you would somehow violate the "contract" of the method specified in its documentation:
Called after the controller's view is loaded into memory.
I am wondering if it is a good practice to implement a deinit on every view controller to check if it is correctly removed when it disappears and avoiding leaking memory?
By default, you don't have to implement the deinit method in your classes:
Swift automatically deallocates your instances when they are no longer
needed, to free up resources. Swift handles the memory management of
instances through automatic reference counting (ARC), as described in
Automatic Reference Counting. Typically you don’t need to perform
manual cleanup when your instances are deallocated. However, when you
are working with your own resources, you might need to perform some
additional cleanup yourself. For example, if you create a custom class
to open a file and write some data to it, you might need to close the
file before the class instance is deallocated.
Swift Deinitialization Documentation - How Deinitialization Works Section.
Usually, when working with View Controllers it seems that there is no need to do such an implementation. However, as mentioned in #rmaddy's comment, it is still an approach for tracing memory leak or reference cycle with the view controller.
If your purpose is to check if the controller has been removed from the hierarchy (view controller life cycle), you could implement viewWillDisappear(_:) or viewDidDisappear(_:) methods; Note that calling on of these methods does not guarantees that the deinit will be called, i.e it does not mean that disappearing the view controller always leads to deallocate it (related: Deinit never called, explanation for deinit not called).
Also:
these Q&As should be useful:
When should I use deinit?
how to make deinit take effect in swift
Understand deinitialization and inheritance in swift language
Swift automatically deallocates your instances when they are no longer needed, to free up resources. So to add deinit on all your viewControllers seems unnecessary. You should call deinit whenever you need to do some action or cleanup before deallocating an object.
Well during the tests phase it maybe good idea, because you can check if everything is good (eg. if you have a lot of completion handler) but overall it is unnecessary.
I am really struggling in understanding what to do in didReceiveMemoryWarning. From what I read on StackOverflow and blogs, the following is my understanding -
Generate all data that your view needs in viewDidAppear and destroy those (set to nil) in didReceiveMemoryWarning. This sounds good to me because those properties can be recreated in viewDidAppear.
However, the problem is that didReceiveMemoryWarning is also called for the view which is currently visible. In this case, obviously I would not deallocate data for the view. Shouldn't didReceiveMemoryWarning NOT be called for the view which is currently visible. But that is not case - How can one handle this?
The Idea with didReceiveMemoryWarning is that any Data that you have that can be re-created, should be deallocated and created from that point on whenever the user needs it. Traditionally, this doesn't include UI components.
So unless you have a ton of UIControls on your screen, it's probably not worth it to write the code to re-cycle, or recreate them (especially since this is done for you in UICollection's and UITableView's). That said, if you're getting a didReceiveMemoryWarning, it's probably because you're keeping some stuff in memory around that you don't need.
I every time use dealloc for remove observer, but just faced with this link that describes that we can use viewWillDisapper instead of dealloc.
The choice is a matter of properly pairing the addObserver/removeObserver calls.
If you call addObserver in a place that is called once such as some init method or viewDidLoad then call removeObserver in dealloc.
If you call addObserver in a place that is called multiple times like viewWillAppear then call removeObserver in viewWillDisappear.
It's the proper pairing that matters.
One critical thing about KVO is that you MUST match removeObserve and addObserver calls and you cannot add duplicate observers. This means that you must carefully think through where you add the observer and where you remove it so that you don't violate either of those restrictions.
If you add it in viewDidLoad it is currently sufficient to remove it in dealloc (since viewDidUnload isn't used any more), but the observer may trigger when the view isn't visible. If you're running on older OS'es where viewDidUnload is still called, this can also be problematic as you have to track when the observers are in place and when they aren't.
You can add it in viewDid/WillAppear, in which case you need to remove it in viewDid/WillDisappear. This is usually cleaner since the calls are (generally speaking) guaranteed to be matched up.
The link you reference is relatively informative and accurate.
I think of it in these terms:
a) does the VC need to be notified while it's on screen only? use viewWillAppear/viewWillDisappear.
b)does the VC need to be notified as long as it's alive (but not necessarily on screen)? use init or viewDidLoad and remove in dealloc.
I've breakpointed my dealloc methods under ARC and they're called when expected. However viewDidUnload is not called.