I am new to iOS and wondering how to properly implement view controller unloading/reloading.
Right now my app has a NavigationController with a MainMenuViewController (custom view controller) set up as root view controller. During the course of app lifetime, new ViewControllers are pushed/popped on the Navigation Controller. This works fine, the appropriate ViewControllers are initiated (from NIBs) the first time they are pushed to the stack.
However, I now want to unload one particular ViewController when it is popped, then reload it automatically when it is pushed again.
I have added a [self release] to that ViewControllers viewDidDisappear: and it unloads, but when i try to push the view again, i get a message sent to dealloc'ed instance error and crash. Therefore, my questions are:
Is that a proper way to unload a popped ViewController?
How to check if a given ViewController is loaded or not?
How to force a reload? With loadWithNib:, then push onto navigation stack?
Regards,
Peter
Welcome to iOS programming. Your crash is a memory management issue. It may take you a bit to get the hang of it but memory management gets way easier if you just follow one rule:
an object needs to release anything it retains (alloc is equivalent to retain)
In this case, your view controller is releasing itself and it definitely did not retain itself. Here's how the sequence works with a navigation controller:
The navigation controller is initialized with a root view controller (the first one on its stack). Lets call this firstViewController
A user action tells firstViewController to initialize secondViewController and push it onto the navigation controller. In most cases, firstViewController will release the instance of secondViewController after pushing it. At this point, firstVC is done with secondVC. The navigation controller is now retaining secondVC
User touches the back button on the navigation bar of secondVC. The navigation controller will pop secondVC from the stack and release it. As long as no other object is retaining it, secondVC will get be dealloc'ed.
Now user is back in firstVC. They can do the same user action which will init and push a new instance of secondVC.
Hope that helps a bit.
I'd also recommend you (re)read the Apple docs and look at the sample code referenced in the framework docs.
http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/Introduction/Introduction.html
Related
I have a two UIViewControllers: loginViewController and MainViewController.
My app logic is like this:
In didFinishLaunchingWithOptions, I check if the user is already logged in.
If yes, I go to MainViewController.
If no, I go to loginViewController to let him login and then go to MainViewController.
When user is in MainViewController, he can log out. When he logs out, he will go to LoginViewController.
All the viewControllers are programmatically created. So when user is in MainViewController, when he clicks logout button, I created a new loginViewController and set rootViewController to it.
My question is:
If the user go to MainViewController through loginViewController, MainViewController.presentingViewController is loginViewController and loginViewController.presentedViewController is MainViewController, will they be freed at some point or they will always stay in memory when I set rootViewController to newly created loginViewController?
Since the logout button may not necessarily be in MainViewController, how can I go to loginViewController from logout button's viewController? If I use presentViewController, will there be too many duplicated viewController created? If I set rootViewController to loginViewController, how will other viewControllers be freed if they have strong reference to each other through presentingViewController and presentedViewController?
I'm little confused about the lifecycle of viewControllers...
Sorry about the long description...
Thanks,
-Lee
To answer your first question "will they be freed at some point or they will always stay in memory when I set rootViewController to newly created loginViewController"
They will be freed (unless they are retained by something not mentioned in your post). If loginViewContorller is the window's rootViewController and has mainViewController presented on it, then if you create a new loginViewContorller and replace the window's rootViewController with it, then the old loginViewContorller and mainViewController will be freed.
To answer your second question.
Don't worry. If at any point in time you replace the window's rootViewController with a new view controller, then UI will instantly switch to the newly created view controller and all other view controllers will be freed.
Think of it as a tree. Window's rootViewController is the trunk. All other view controllers are branching from it. So if you replace rootViewController with a new view controller, the whole hierarchy is freed again assuming no other object keeps a strong reference to any of the view controllers
I have a LoginViewController (UIViewController) that when all the criteria is met and the user hits the Login button, a storyboard segue is run that pushes the ProfileViewController (UIViewController). When this happens, I have a log statement in my LoginViewController's dealloc method to see if it is called and to my disappointment it is never called. My question is whether or not it is supposed to be called? Also, when I log in, sometimes I get a "Received memory warning" and sometimes I do not which I find strange because I am taking the exact same steps in both cases and yet i get a memory warning one time and not with the other.
Anyone can shine some light on this that would be great!
Thanks.
UINavigationController maintains a stack of view controllers. You start with one element, a LoginViewController, on that stack. When you push a ProfileViewController, you now have two elements on the stack. The LoginViewController can't be deallocated until it is removed from the stack.
If you want the ProfileViewController to replace the LoginViewController on the navigation controller's stack, you can write a custom segue class to implement that behavior. See this Q&A.
(You might think you could use the “Replace” or “Show Detail (e.g. Replace)” segue type in your storyboard, but those only work if you are using a UISplitViewController.)
With ARC enabled, when a object is not referenced, it will be released.
In order to display view from ProfileViewController, you instantiate a object of it in LoginViewController, and that's how you still can see the profile view after it is presented. If LoginViewController instance is released, the profile view will also get released(assume no one else references it). For the same reason, the LoginViewController instance is not released because another object is holding a reference to it. Say your views are presented in Window -> ProfileViewController -> ProfileViewController, it's the window that keeps ProfileViewController instance from being released.
If you have two views as I assumed so far, the memory warning should be from somewhere else. Two views cannot cause the issue.
Is it bad practice to skip view controllers in a navigation stack?
In my example I have a UINavigationController (which is being presented modally) with three UIViewControllers (firstViewController, secondViewController and thirdViewController), most of the time the user will be accessing firstViewController, maybe tapping a row which will then load secondViewController and so on, but what if in some circumstances I want to load the second or third view controllers immediately but still keep the first view controller in the stack, so that the user can still go back to the firstViewController.
I can do this but it doesn't feel right - is this something I should avoid doing?
Yes, it's called deep-linking, and it's perfectly fine. It's quite commonly done for things like coming from an external URL or push notification.
I've got a UIViewController with two button, prev and next. When I push these button I re-instantiate the same viewcontroller passing it some variables and then I push it on the UINavigationController with no animation.
With this method my memory is always full cause, as far as i know, it is released only when I pop my viewcontrollers.
So my question is.. how could I implement this behavior? I need to free the memory when I load next (or prev) view controller
Build a custom Navigation controller and animate the transitions yourself.
I am new to iOS programming, i read the view controller programming guide but some things are still unclear to me
View of a viewcontroller is destroyed but viewcontroller remains, there nothing specified regarding if I my self want to destroy any viewcontroller or I just missed it. VC guide says iOS destroy VC when memory start getting short for other resources and same goes for VC's view right? Need to understand memory management of VC, please read my second point.
In a uinavigationcontroller i have a tableviewcontroller which display list of my friends, on selecting any of my friend i am pushing another VC of chat. The problem is every time on selecting a friend do i have to create a new instance of chat VC? how this chat VC will be unique for every friend i am trying to chat with? I am also saving messages in DB so who ever friend window open or close messages shows up, user don't get a blank view. Is it possible only one instant of chat VC is used for every friend uniquely(means only message of particular friend show and send to him to whom i am trying to chat). I think this problem is related to passing data between viewcontrollers but i am confused.
I hope i have clearly define what i am confused in. Please ignore any silly mistakes.
Thank you.
Answer to your queries -
View of a viewcontroller is destroyed but view controller remains
According to UIViewController guide -
It is responsible for creating those views and for relinquishing ownership of them at the appropriate times, including during low-memory conditions and when the view controller itself is released.
So we need to take the ownership to release a view controller.
Also The UIViewController class provides some automatic handling of low-memory conditions through its didReceiveMemoryWarning method, which releases unneeded memory.
In your Scenario-
TableView -> on click of cell push next view -> now pop this view to go back to table view(here memory got release) -> in table view again -> on click of cell a new view controller is pushed.
In this way it handles memory. If in a hierarchy we are pushing some views like -
VC1 -> VC2 > VC3 -> VC4
Now in this scenario navigation controller keeps instances of all these view controller, as navigation controller gives us flexibility of moving backward. And as soon as we go back that view controller is removed from stack and memory allocated to that is freed.
Syntax used -
Push a view controller -
[self.navigationController pushViewController:myViewController animated:YES];
[myViewController release];//at the time of push we add this release statement.
Now when we do a pop, its dealloc method is called and this is removed from the stack.
[self.navigationController popViewControllerAnimated:YES];//That view is release from navigation stack
Hope this clears your doubt.
Checkout this developer page for more details - http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ViewLoadingandUnloading/ViewLoadingandUnloading.html