Presenting multiple view controller modally -- A memory issue? - iphone

How much memory a view controller eats when it is loaded into the memory?
I have an application where I am planning to keep 4 view controllers in the memory as they are modal on each other and out of them, one view controller is a navigation controller (with 4-5 view controller pushed in stack) presented as modal.
Any suggestions?

A view controller object itself usually needs very little memory unless you use it store large objects like images or caches. What takes a lot of memory is the view that is attached to the view controller. That's why view controllers unload their views when they receive a memory warning and the view is not currently on screen.
So you shouldn't worry. Follow the memory management rules, implement viewDidUnload correctly (release your outlets) and respond to memory warnings appropriately. The view controllers will take care of unloading and reloading their views if needed.

Related

Storyboard Controllers not Deallocating

My Initial View Controller of the storyboard load another view controller using performSegue:withIdentifier method which in turn loads some other controller using same performSegue:withIdentifier method.
However, neither the initial view controller nor the second view controller are deallocating. They both tend to have a reference count of 1 as seen via instruments.
I need to send user back to first controller when he logs out of application. The only way I have figured so far is to use performSegue:withIdentifier method and send the user back to initial controller.
However, it concerns me that previous controllers will not have been deallocated thus, resulting in re-creation same view controllers.
Since I need to logout a user back to first screen, I want to make sure that all previous view controllers have been deallocated.
When you perform a push or modal segue, it will not (and should not) release the view controller from which you're seguing. It needs to keep it so that when you pop/dismiss back to it, it will still be there. The exception to this rule is when using a split view controller and you use a replace segue. But that's a special case.
If you want to go back to the first scene, if you're using a navigation controller and using only push segues, you can use popToRootViewControllerAnimated. (For iOS 5 targets, I'll always use navigation controller, and hide the navigation bar if I don't want it visible, for that reason. It's convenient to be able to pop back multiple levels. It's cumbersome to achieve the same effect with modal segues.) In iOS 6, you can use an unwind segue, in which you can pop/dismiss back an arbitrary number level of scenes, for example, to return to your initial scene.
Looping with performSegue is not a good idea..
If you have to go back in your VC hierarchy, you should either use a UINavigationController with pushing/poping VCs, or presenting/dismissing a modal VC. You can combine both by modally presenting a UINavigationController.
Prior to iOS 6 A UIViewController will stay alive but its more expensive UIView will be deallocated to save memory. The UIViewController itself is pretty light compared to a UIView.
Since iOS 6 you should according to the documentation override didReceiveMemoryWarning
Docs for UIViewController:
Memory Management
Memory is a critical resource in iOS, and view controllers provide
built-in support for reducing their memory footprint at critical
times. The UIViewController class provides some automatic handling of
low-memory conditions through its didReceiveMemoryWarning method,
which releases unneeded memory.
Prior to iOS 6, when a low-memory warning occurred, the
UIViewController class purged its views if it knew it could reload or
recreate them again later. If this happens, it also calls the
viewWillUnload and viewDidUnload methods to give your code a chance to
relinquish ownership of any objects that are associated with your view
hierarchy, including objects loaded from the nib file, objects created
in your viewDidLoad method, and objects created lazily at runtime and
added to the view hierarchy. On iOS 6, views are never purged and
these methods are never called. If your view controller needs to
perform specific tasks when memory is low, it should override the
didReceiveMemoryWarning method.
As long as you manage you correctly react (depends on the iOS Version) and dealloc the view I see no problems here.

Why is it incorrect to insert the view from one view controller into another?

In an iPad application I have a tab Controller containing several view controllers. One of these view Controller (call it MainViewController) needs 2 table views side by side.
So I wrote 2 UITableViewController subclasses and from MainViewController, I alloc/init both subclasses of UITableViewController, and add the tableview from each to the MainViewController's view.
This means that UITableViewController subclasses's views are subviews of MainViewController's view.
This answer: https://stackoverflow.com/a/7684648/191463 says that doing that is incorrect and it seems Apple are starting to cut down on it.
I really do not want to have to put all the code from both UITableViewControllers in MainViewController, as it will make it much harder to read and in future could create duplicate code, if I want to use one of the tableview elsewhere in the app by itself.
Is this actually a problem, if it is how do I do it properly?
Apple isn't cutting down on it. This is the only way to create custom container view controllers prior to iOS 5. Apple actually listened to the developers and made it easier to do this sort of thing in iOS 5 with child and parent view controller methods, not to mention they made it so it worked hierarchically.
In most cases, this wouldn't actually be a problem in terms of applications crashing or performance or anything. It can be a problem in some cases, because let's say you have a child view controller. You add the view controller's view to your root view controller. Prior to iOS 5, child view controllers were things like navigation controller view controllers, tab bar controller view controllers, and modal view controllers. What happens when you have a button that calls [self.parentViewController dismissModalViewControllerAnimated:YES];? Technically, the view controller isn't being presented as a modal view controller, you added the view to the root view controller view.
In iOS5, you're able to add child view controllers to view controllers and are able to transition from one child view controller to another.
Now even if your view controller doesn't have a different parent, adding a "root" view controller to another root view controller isn't the best way to do it (especially since you don't get access to the parent view controller unless you explicitly create a parentViewController pointer in the child view controller). So in the end, Apple just made it easier and more decoupled.
It is OK to do it so long you take the responsibility of managing the viewController life cycle events
initWithNibName...
loadView:
viewDidLoad:...
.
.
viewDidUnload..
dealloc
memoryWarnings
orientation changes
So if you create a custom "container view controller" it becomes your responsibility to call all these methods on child viewControllers at the appropriate time. Think of it as "If you were to implement UITabBarController" what all will you have to manage regarding the children ??"
It quickly gets complex. Adding another viewController's view as subview is childs play.
iOS 5 does some of this stuff for you by specifying parent child relationship, however I still haven't seen any sample code anywhere yet to point to.
I'd say it is not incorrect or wrong to create view controller containers, especially when Apple engineers do that themselves. UITabBarController, UINavigationController or UISplitViewController - they are all view controller containers. More over many great apps with unique UX do that more common than you think. However the real issue is that it's quite hard to do it the right way, so e.g. view lifecycle, memory management and rotation handling is done properly along the hierarchy of views. Fortunately Apple guys did a decent job and iOS5 introduced lots of functionalities regarding controller containers:
UIViewController class reference
View Controller Programming Guide for iOS
If you're interested how above problems had to be addressed before iOS5, read these two very good blog posts:
Writing high-quality view controller containers
Using view controllers wisely

How to unload view of UIViewController

I have custom control like tabbar which displays many controllers some of them also have my tabbar with other controllers, so, my app uses lots of memory because every controller is stored in memory. So I want to unload invisible controller, but I not found any method for unloading UIViewController. How can I do it?
PS. I can not use UITabBarController, really can't.
You don't. UIViewControllers don't get unloaded in low memory, just their views do.
This happens in didReceiveMemoryWarning on your own view controllers and gets called automatically when a low memory warning occurs.
Override this and unload anything that can be re-created in viewDidLoad.
Remove the view controllers view from it's superview and release the controller. Job done.
I know it's an old thread but might be usefull for someone.
You can unload viewControllers view by calling:
[viewControllerWhoseViewYouWantToUnload didReceiveMemoryWarning];

Force loading/unloading a view

How can I setup a view controller to refresh the fields in the view only it is pushed on a navigation controller and releases the objects when it is popped from navigation controller views.
My goal is that for a view controller:
Refresh display only when it is pushed on the navigation controller stack
releases data when it is popped from the navigation controller stack
reuse the controller as much as possible (to eliminate xib files loading/unloading overhead)
Are there any delegate methods for viewWillBePushed: and viewWillBePopped:?
Example
Let's say I have three layers of views:
Movies (MovieListController)
--> Movie Basic Info (MovieInfoController)
--> Movie Credits (MovieCreditsController)
Assume we have a class Movie to represent each movie.
Here, I want to only have three UIViewControllers, one instance of each in the runtime of the program. So I can reuse the same MovieInfoController for different movies and similarly MovieCreditsController.
However, I would also like MovieInfoController to only update its view fields (e.g. title, release date, etc) only when it is pushed on the stack. Implementing the display in viewWillAppear will force it to update fields even when MovieCreditsController is popped.
Also, I want MovieInfoController to release its reference to Movie, when it is popped, and any potentially large object (e.g. cover image). Implementing this logic in viewWillDisappear will cause it to release the data when it is pushing MovieCreditsController on the stack.
Is there reasonable? Am I going about it the wrong way?
You're 90% there because you created a Movie object. MovieListController should own a single MovieInfoController. It should call -setMovie: prior to pushing it onto the navigation controller. The call to -setMovie: should be what updates your fields.
Similarly, MovieInfoController should own a single MovieCreditsController, and should call -setMovie: on it before pushing it onto the nav controller.
In MovieListController and MovieInfoController, you can call [subController setMovie:nil] in their -viewDidAppear: to free up memory.

How to re-load UIView after memory unload?

My main view controller (representing the Main Menu in my app) has a simple UIView with a few sub views. I am using a modal-type design pattern and switch to multiple other view controllers before finally returning to the main menu. The problem is, in my other view controllers (not the main menu one), I often load data-heavy images and the like which sometimes causes the main menu (which is not currently on-screen) to unload its view to in response to memory warnings. The problem is, when I ultimately switch back to my main menu, the screen is all black and all but a few UILabels have been dispensed of. At this point, I would like to re-load my view and start fresh. But in the documentation, it says that you should never call -loadView directly. How can I re-load my view?
In this situation, check the isViewLoaded property — it should be returning NO. If the view has been unloaded correctly, isViewLoaded will return NO and calling view on your view controller will automatically load in the view again.
Check your custom view unloading code — you should be removing everything from the view hierarchy ([[self view] removeFromSuperview]) that belongs to you.
If you haven't got any unloading code, make sure you're not over-retaining some of your views, which will cause problems when unloading.