deinit not called inside uiviewcontroller after pressing back - Swift - swift

I am trying to dealloc / destroy an NSTimer after a user clicks back, but the deinit{...} inside the uiviewcontroller never gets called.

Beware that a view controller going out of the screen does not mean that it will be deallocated afterwards. I would recommend moving the timer dealloc to viewDidDisappear, but obviously it depends what you are using that timer for as well.

Related

In iPhone, is it guaranteed that viewWillAppear, viewDidAppear, viewWillDisappear, viewDidDisappear are always called?

I'm using viewWillAppear, viewDidAppear, viewWillDisappear, viewDidDisappear of UIViewController.
And I use UINavigationController to navigate view controllers.
For example, I create NSTimer or register notifications in viewWillAppear or viewDidAppear. And I invalidate the timer or remove notifications in viewWillDisappear or viewDidDisappear.
But if those 4 methods (viewWillAppear, viewDidAppear, viewWillDisappear, viewDidDisappear) are not called, the program will crash or retain cycle will happen.
I couldn't find the documentation saying that it is guaranteed that viewWillAppear, viewDidAppear, viewWillDisappear, viewDidDisappear are always called.
Till now, they seem to be always called and my program works as I expected.
But are there cases that the 4 methods are not called?
Or is it possible the 4 methods are usually called but sometimes not called randomly with no reason?
I can say than viewWillAppear and viewDidAppear methods are always called in your view.
viewWillDisappear and viewDidDisappear methods are called when you change your view with another one or close it. If you have some scrolling views for example, these methods won't be called.
Yes, they are always called. Just make sure to call the superclass implementation if you subclass one of your view controllers.
I think you can trust those methods, still, viewDidLoad and dealloc for me never failed.
You can register and remove notifications there. But, I don't know all of your app specifications and what I recommend might not be viable.
Yes the View events always fire, however to override the behaviour you must declare them.
According to apple documentation viewDidUnload is deprecated.
Look at this link for the official apple class reference for UIViewController
UIViewController Reference
It states:
The UIViewController class provides specific methods that are called when these events occur. Subclasses can override these methods to implement specific behaviors.
Here are the same thread in which same thing asked m any times
you can view these here
here the first link
here the second one
Aside from bugs when some of the methods may not get called and/or called twice (e.g. dismissing/popping several controllers at once), with iOS 7, if you initiate the swipe-to-back gesture, partially revealing the previous controller in the navigation stack, and then cancel the swipe-to-back, so that no popping occurs, you will observe that not all of the methods get called on both of the controllers.

Modal View doesn't release itself after dismiss

I've been facing a weird issue with dismissing a modal view.
I present a modal view like this:
ResepiDetail *detail =(ResepiDetail*)[[ResepiDetail alloc]init];
[self presentModalViewController:detail animated:YES];
and dismiss it like this with a back button:
[self dismissModalViewControllerAnimated:YES];
after this the view dismisses itself and goes back to the previous view, but it doesn't release itself from memory. I found it out by sending a notification message and that view received it. Additionally I tried to track the VM memory Allocation, and it seems the view is still in memory.
I'm using ARC and have the same method used for another view which works perfectly.
The code is fine, as posted, so here's some hints on how to proceed:
A sure fire way to be certain your view controller hasn't been deallocated is to override dealloc and log something identifiable. You can still do that in ARC, just don't explicitly call super. If you don't see the log when you expect to, then you have a problem.
Assuming that you have determined that you absolutely do have a problem, then the issue becomes finding the retain cycle. If the issue is that an instance of ResepiController isn't being dealloc'd, then you need to look for...
Any code outside the ResepiController class that has a strong reference to it. For example, if your class signs up as the delegate of some other class, make sure the delegate isn't using a strong reference.
Any internal blocks that may have implicitly retained self. Are there any blocks anywhere in your program that may have a reference to your controller at the time that you think it should be released?

ViewWillDisappear versus dealloc

I put an NSlog in my dealloc method of my view controller. It does not get consistently called. I do notice that ViewWillDisappear does get called always. Would it be ok to move all my tidy upcode here? Setting stuff to Nil and release calls.
Anybody got some advice on why dealloc is not getting called? I know it says in docs it may not get called, but if you have a very simple App it gets called always. So something i do must be affecting the dealloc.
This is the code that calls my ViewController than isnt always calling my dealloc.
-(IBAction) playComputerTapped:(id)sender
{
PlayGameViewController *pgvc = [[PlayGameViewController alloc]
initWithNibName:#"PlayGameViewController" bundle:[NSBundle mainBundle]];
pgvc.gameMode = 1;
[self presentModalViewController:pgvc animated:YES];
[pgvc release];
}
The above code takes me from the mailmenu ViewController into the game.
Below is the code to leave the gameViewController and take me back to the menu.
[self.parentViewController dismissModalViewControllerAnimated:YES];
Thanks
-Code
Don't you mean viewDidUnload instead of viewWillDisappear ?
viewWillDisappear is called when the view controller is disappearing. This usually happens when the view controller is being popped out, or other view controller is pushed to the stack. Purpose of viewWillDisappear is to stop active actions - for example stop animations, hide some elements or similar.
viewDidUnload is probably what you meant as this one is called when view controller's view is unloaded. This should never happen for a view controller that is currently visible, only for controllers that are somewhere in the navigation stack (part of UITabBarController or UINavigationController), but not currently visible. Purpose of viewDidUnload is to release any UI elements that are part of the view and that the view controller retained as well.
To understand it's important to realize the reasons why a view for such controller would want to be unloaded. Reason is memory consumption. Views consume considerable amount of memory, even when they are not currently visible. Views are usually very easy to reconstruct - simply by calling the code that constructed them in the first place. That's especially very easy if you are using Interface Builder. So these views are best candidates to be freed to gain more memory.
When system doesn't have enough memory it starts calling didReceiveMemoryWarning method of view controllers which are not currently visible. If you have created your controllers from template in Xcode, this method simply calls super's (UIViewControllers's) implementation [super didReceiveMemoryWarning]. That default implementation will release self.view, which in turn should deallocate it together with all its subviews.
Now let's say that your view-controller needs access to some of the subviews to manipulate it in some way. For example you can have a UILabel element there and you want to change its content accordingly. To access this element you create a member variable (IBOutlet) and connect it to the element. Now your view-controller owns that label, so its retain-count is increased. When controller's view is released, so is the label, but because your view-controller still retains the label, it will not be deallocated. Therefore you should release the label in viewDidUnload method.
I've seen applications that were creating views programmatically (in loadView method), but loading was done in such dirty way that it was not possible to reconstruct the view after it was once deallocated. Therefore each time system was out of memory, it had called didReceiveMemoryWarning which in turn deallocated the view, but after navigating back to that view-controller application had crashed. A fast "bugfix" was to remove calling [super didReceiveMemoryWarning] in view-controllers. Well, system didn't get the memory and some strange effects occurred, but at least the application didn't crash immediately.
Now the third one - dealloc. This is called when object is not owned by anyone and its memory is going to be freed. Here you need to release all objects that you have retained. For view-controllers those are usually references to model classes.
I want to describe one more possible scenario. Let's say you have a view-controller displaying a chat with another person. Let's say it's very fancy chat, with emoticons and buddy-icons being animated. Let's say that each chat-entry is displayed as a cell of UITableView.
When your buddy sends you a message, you want to append a new cell into table-view by reloading it. Therefore your view-controller has an outlet to the table-view.
In viewWillDisappear you should stop the animations of emoticons and icons.
In viewDidUnload you should release the table-view.
In dealloc you want to release chat's history (probably NSArray of all messages sent and received during this conversation).
Now if you navigate away from your chat, viewWillDisappear gets called and you stop the animations.
When system is low on memory, and your view-controller is not visible, didReceiveMemoryWarning gets called and the view is released. Your viewDidUnload gets called and you release UITableView so that it can be really deallocated.
When you navigate back to the chat, loadView gets called again and your view is constructed again, viewDidLoad is called after that. Your model (representation of chat conversation) is still there, so the table-view's datasource has all the data as before, so table-view will display exactly the same thing as before the view was deallocated. After all viewWill/DidAppear is called where you start the animations.
When you finish chatting with your friend, you release the view controller, and it gets deallocated - dealloc is called, you release an array containing the chat messages, and cleanup everything else.
I hope it make things a little clearer.
Well that depends.
If you need to reduce the memory footprint of your app, unloading stuff while the view is not visible via viewWillDisappear is a good way to go. However you'll need to re-initalize everything once the view will be shown again, depending on its content this may produce considerable overhead - maybe even without the need.
For a small app (in terms of memory usage), using dealloc to unload your stuff is fine. It will be called only if the objects retain count drops to zero and is the last method that will be run before the object is destroyed. When using autorelease, that may not be the case right away and of course the object could be retained by some object other than the parentviewcontroller, preventing it from being destroyed and thus dealloc from being called.
You might want to check out the Memory Management Programming Guide, it'll explain things more detailed.

Why is viewDidUnload called less often than viewDidLoad?

I put NSLog(#"%#::%#", [[self class] description], NSStringFromSelector(_cmd)); in both viewDidLoad and viewDidUnload of a view controller.
In the log, I found viewDidLoad is called a lot more than viewDidUnload when the app moves to and from different .nibs.
Why?
The viewDidLoad and viewDidUnload is not corresponding to each other.
The viewDidUnload will only be called when you receive a memory warning. The system then will automatically call your viewDidUnload.
In the normal case, when you push a MyViewController and pop it out. The life cycle will happens like this:
init
viewDidLoad
release
That means, whenever you init and push/present a view, a viewDidLoad will be called. But when you pop the view, the release will be called in normal case and viewDidUnload will be called in memory warning case.
This is quite implicit and Apple doesn't state it clearly on the Guide. Here is some reference: Load and Unload cycle
I imagine that in the cases where -viewDidUnload wasn't called, the view controller was released.
viewDidLoad: controller loads view
viewDidUnload: memory warning, controller unloads view
viewDidLoad: controller loads view again
-: controller gets released, doesn't explicitly unload the view
You and up with 2 -viewDidLoad calls and 1 `-viewDidUnload' call.
Maybe also put a NSLog into the -dealloc method and see if the number of -dealloc and -viewDidUnload calls combined matches the number of -viewDidLoad calls.
when a new view loads, the old view can still be loaded in the background.
you are searching for viewWillAppear as conterpart i think.
views only unload in case of a memorywarning.

How to solve this performSelector:withObject:afterDelay: problem?

I have a view controller, which calls performSelector:withObject:afterDelay. However, if I remove that view controller right after calling this, my app crashes as soon as the system tries to perform the delayed selector on that (deleted) view controller.
Now how can I go about this? I need to get rid of the view controller to save memory, so there's no way to let it hang around.
Any way to cancel a delayed perform selector before it performs?
I suggest to use an NSTimer instead. You can simply invalidate the timer to make sure it will never be called after the UIViewController has gone away. A good moment to invalidate the timer is for example in viewWillDisappear:.
This does mean that the timer is owned by the view controller. But that is a good design anyway.
You can't perform a selector on a deleted object, you either need to have the object around, or do the work with some other smaller object that you can have hanging around.
To cancel there is a cancelPreviousPerformRequestsWithTarget:selector:object: or cancelPreviousPerformRequestsWithTarget: method.