Memory usage: when does it make sense to use a UINavigationController and when not? - objective-c

This question has been on my mind ever since I started with iOS development: the UINavigationController and the usage of memory.
I see lots of apps like iMail, Find Friends, Notes, etc. where the UINavigationController makes perfect sense. They allow you to drill down two or three levels of hierarchy and that's it.
But imagine browsing a deep hierarchy, like your Mac's file system, starting from the root folder.
If I used a UINavigationController, I would keep on pushing hundreds of controllers on the stack (worst case). I don't consider that a good usage. There can be cases where the pushed controllers can become very heavy (as far as memory is concerned) and they just sit there for nothing.
I would wish there was a "dynamic" version of the UINavigationController: it would just tell you what to create when navigating back up the hierarchy instead of just popping up the hierarchy.
My question is now: is UINavigationController meant to be used for deep hierarchies? What are good alternatives if you want all the animation, bar items, etc?
Or do I see issues where there aren't any?

UINavigationController is dynamic. When you get low on memory, you can release the memory used by parts of the hierarchy that are not visible -- this is a manual step when you get a low memory notifiction -- and the OS automatically releases the views in the same situation.
When the top view controller is popped off the stack, your viewDidLoad method would be called, allowing you to recreate your view.
If it really is a problem, iOS5 allows you to create your own "container" views, so you could create your own navigation controller that does exactly as you suggest. Check out the "Implementing a Container View Controller" section of the UIViewController documentation.
Having said that, you might need to add some shortcuts to work your way around a very deep hierarchy. The UI could get painful if you can only go back one screen at a time.

Related

Understanding the use of addChildViewController

I'm working with some code that I need to refactor. A view controller is acting as a container for two other view controllers, and will swap between them, as shown in the code below.
This may not be the best design. Swapping the view controllers in this way might not be required. I understand that. However, as I work with this code I want to further understand what happens with the addChildViewController call. I haven't been able to find the answer in Apple's docs or in related questions, here (probably an indication that the design needs to change).
Specifically - how does the container view controller handle a situation where it is asked to add a child view controller, which it has already added? Does it recognise that it has already added that view controller object?
E.g. if the code below is inside a method - and that method is called twice...
[self addChildViewController:viewControllerB];
[self.view addSubview:viewControllerB.view];
[viewControllerB didMoveToParentViewController:self];
[viewControllerA willMoveToParentViewController:nil];
[viewControllerA.view removeFromSuperview];
[viewControllerA removeFromParentViewController];
Thanks,
Gavin
In general, their guidelines for view controller "containment", when one contains another, should be followed to determine whether you will need to implement containment.
In particular, worrying about adding the same child view controller twice is like worrying about presenting the same view controller twice. If you've really thought things through, you shouldn't need to face that problem. Your hunch is correct.
I agree that Apple's docs should be more up-front about what happens with weird parameters or when called out of sequence, but it may also be a case of not wanting to tie themselves to an error-correcting design that will cause trouble down the road. When you work out a design that doesn't ever call these methods in the wrong way, you solve the problem correctly and make yourself independent of whatever error correction they may or may not have - even more important if you consider that, since it's not documented, that error correction may work differently in the future, breaking your app.
Going even a bit further, you'll notice that Apple's container view controllers can't get in an invalid state (at least not easily with public API). With a UITabViewController, switching from one view controller to another is an atomic operation and the tab view controller at any point in time knows exactly what's going on. The most it ever has to do is remove the active one and show the new one. The only time where it blows everything out of the water is when you tell it "you should blow everything out of the water and start using these view controllers instead".
Coding for anything else, like removing all views or all view controllers no matter what may in some cases seem expedient or robust, but it's quite the opposite since in effect one end of your code doesn't trust the other end of your code to keep its part of the deal. In any situation where that actually helps you, it means that you've let people add view controllers willy-nilly without the control that you should desire, and in that case, that's the problem you should fix.

When do I need to subclass UIViewController and when can I just instantiate it?

I am learning iOS programming through the Big Nerd Ranch guide by Hillegass and Conway. I’m writing an app of my own as I go through the book, and one of the questions that has been bugging me is exactly when I need to subclass UIViewController (and its ilk) and when I can just instantiate it.
For example, my app consists of generic building blocks: the interface is tabbed, and the tabs lead to a UITableView, a UINavigationController that creates UITableViews, and so on. Following the book’s instructions, I have subclassed UITableViewController to create the table views. However, in creating the UITabBarController that contains all of my app’s content, it seems sufficient to instantiate a UITabBarController and then add a bunch of views to it. (All of this is done in the application:didFinishLaunchingWithOptions: method of my app delegate. Since most of my app consists of simple combinations of basic UI parts, I’m trying to do build the UI programmatically whenever possible.)
I get the impression that what I should be doing is creating a subclass of UIViewController (or UITableViewController or whatever) for every interface in my project. This seems weird to me, since most of these classes would only ever be instantiated once. Am I just misunderstanding how OO should be used in this case? (I have a good amount of programming experience but relatively little has been with OOP.) Should I be creating a subclass for each and every screen that the user will see?
Should I be creating a subclass for each and every screen that the user will see?
If each view requires different logic, yes.
Don't shy away from creating new classes for conceptually separate things. Programmers coming from non-OOP to OOP might feel that a file with only a small amount of code is a waste. Suppress this feeling. Classes are cheap, and help enormously to organise your thinking.
So you have two types of UIViewControllers in iOS. "Container" viewControllers and "Content" viewcontrollers. Both are subclasses of UIViewController but have very different purposes.
The Container type is what the UINavigationController and UITabController are. They are rarely subclassed and typically used as is (in fact, I believe Apple doesn't allow the subclassing of UINavigationController at all). These "Containers" take care of moving "Content" view controller around for you. They do not have much content of their own, beyond adding things like a tab bar or a navigation bar.
The "Content" view controller are the ones you create most of the time. You will rarely be able to use a UIViewController as is, because it will not have any functionality. That is why you subclass them. These are meant to represent a single "screenful" of content. So in effect, every "screen" the user sees should be controlled by a UIViewController subclass.
The UITableViewController is simply a specialized sublass of UIViewController that already contains some methods for managing tables.
The way the UIKit framework was designed was for you to use subclasses of UIViewController to display content and to use out-of-the-box "Container" controllers to facilitate the management of your UIViewController subclasses.
You need a subclass of UIViewController if you want to do any of the following (not an exhaustive list, but some examples)
customize the view hierarchy when the view hierarchy is loaded (in
viewDidLoad)
provide some behaviour as the view controller's views become visible
(or not) (in viewWillAppear:, viewDidAppear:, viewWillDisappear:,
etc.)
clean up after yourself as needed in viewDidUnload
create outlets to views in the hierarchy so you can adjust them as
needed in the above lifecycle methods
My reasoning behind subclassing UIViewController, and other classes is that:
Almost always you must initialize variables and assign values to the instances of classes. You add subviews and set their frames, define actions for the UIViewController instance, etc. If this UIViewController instance is directly from the base class, its initialization should be done outside of it. If this initialization is required at different places for multiple times, you may have to deal with repeated initialization process.
So, you've compiled these processes into a method, making it reusable from wherever this UIViewController instance is used. But where do you want to put it? Don't you think it's much better to put it inside the subclass of UIViewController? Also, you don't even have to come up with specific name for this initialization method. Just override the default -(id)init from the super class.
Though you may think it's suffice to use UIViewController without subclassing it for now, as your project grows, it will be challenged to deal with reusability issues. Take some time to look at your codes. Check if there is too much repetition for such as initializing an object, or assigning values to it. If you are doing same things with an instance of a class in multiple places, compile them into a method to be reused. And as number of such methods grow, you will find the need to use subclass which will contain these relevant methods for the instance.
No matter the size of your project, using classes to distinguish different objects is important. Almost always, the basic essential classification is done by the framework, making it unnecessary to introduce new concept for a class. However, this doesn't mean the framework also knows how your project and its objects can be classified into. By using subclass, you can utilize every benefit the development framework can provide and still keeping the objects in your project to be as unique as possible, based on the purpose you've designed for them.
Well about the UITabBarController you are right. There is no reason for you to subclass anything if the default behavior is sufficient. However once you need to do some custom things you will need to subclass it..
Also, why are you trying to build the GUI programmatically? For the learning curve? There is no real reason not to use InterfaceBuilder, it saves you a lot of time.
You should subclass the UITableViewController in order to get your data in the view, that is how the MVC model works. The default implementation does not offer anything in order to get your data in the view, I don't think they will ever do that in order to make sure that nothing is wasted, their 'connection' to the model object might be different from the one you want and you would end up writing an adapter if your model object is not compatible.
I hope this will help you out a bit.
And merry x-mas.

iOS 3 - UITabBarItems disappear from UITabBar after a memory warning occurs

At a great number of requests from people using older iOS hardware, I'm currently refactoring and optimizing my app so it will work on iOS 3. That being said I've got a glitch with my UITabBar that I can replicate on all of the iPhone 3G units I've tested it on.
The glitch appears to have been fixed in iOS 4, but I was wondering if before that time, anyone else had this glitch as well and had figured out a (relatively elegant) workaround for it.
The problem is what you can see below; when a memory warning occurs and all of the views offscreen are released, when I bring a view controller with a tab bar back on screen, all of the UITabBarItems that are supposed to be in it are gone. As far as I can see, they're not being drawn at all; ie tapping the tab bar has no effect. After setting breakpoints and examining the UITabBar and its items in memory, they're all still there (ie not getting released), just that they're not getting redrawn when the UITabBar is re-created in the controller loadView method.
My app works similar to the official Twitter app in that I implemented my own version of UITabBarController so I could control the integration of it with a parent UINavigationController properly. I set it up as closely as possible to the original UITabBarController class though, with all of the child view controllers handling their own respective UITabBarItems and initializing them inside the class' init methods. Once the child view controllers are passed to my TabController object via an accessor method, the tabBarItems are accessed and added to the UITabBar view.
Has anyone seen this behaviour before and know of a way I can fix it? I'm hoping there's a really simple fix for this since it already works in iOS 4, so I don't want to hack it up too badly.
Thanks a lot!
After a bit of research, I think I found a solution to this. It's not the most elegant solution I was after, but it definitely works.
I'm guessing after a memory warning is triggered, something is happening to the UITabBarItem objects that basically renders them corrupt. I tried a lot of things (flushing out the UITabBar, re-creating the controllers array etc), but nothing worked.
I finally discovered that if you completely destroy the UITabBarItems and allocate new ones in their place, then those ones will work. :)
So my final solution to this was to add an extra condition in the viewDidLoad method of my controller that if the detected system was iOS 3, and there was already an array of UITabBarItems, it would go through each one, copy out all of the properties needed, destroy it, allocate a new one and then copy the old properties over to the new one.
I'm still going to keep an eye out for a better solution (I think there's a bit of overhead in this method), but thankfully at this stage, iOS 3 legacy support is becoming less and less of an issue. :)

Working with many interface elements in Cocoa

My app requires an interface that has many buttons, text fields and matrixes. And they need to change from time to time. Right now I do this by having all elements in IB already and hiding/showing/moving them when needed. What would others recommend? Should I do that? Should I use an NSTabView? NSView? Should create the elements programatically? If so, what if I have an element that is already created that I need again without changes? It would be a waste of releasing it and creating it again.
Any help would be greatly appreciated.
In my opinion, it's better to create interfaces programmatically if you have to animate views around a lot. If it's just a matter of hiding/unhiding them, IB works great, but if you need re-layout or create unknown numbers of views dynamically it's not worth trying to make it all work with nib files.
As for general advice:
Create subclasses (from UIView or UIControl or one of their subclasses) for every kind of element you're going to use. It's tempting to piece together composite views from your UIViewController, but you'll really be much better off creating real classes.
Study the standard Cocoa view classes, and try to create similar API:s in your own controls and views.
Put as much data (sub-element positioning etc) into a plist, so that you can easily change it from one centralized place instead of having to dig around in the code.
If you are often creating several dozen short-lived views, it's worth keeping them in a pool and reusing them. But if it's just a few labels being added and removed intermittently I wouldn't worry too much about it. As usual: don't optimize too early.
Your current approach sounds fine. If you're showing/hiding them but otherwise they remain unchanged, why go through the trouble of creating them with code, when your XIB keeps a "freeze-dried" copy of exactly what you need already?
As long as you're keeping them within logical groups, you can just move/swap/show/hide the group's container (like NSBox or an NSView). If you have a LOT of logical groups, which aren't always shown every session, you can separate them out into their own XIBs and only load them when they're needed, to save launch time and memory.
If you use NSViewController, it's even better because you can make clean breaks for each logical group. Load the panel as the view and the view controller will keep outlets/actions and has a one-to-one relationship with a xib.

Efficiency of create views programmatically vs IB

I have a large number of UIViews that are created in the app delegate applicationDidFinishLaunching and added to an NSMutableArray. The user pages through these views using a page control and scroll view. I created the UIView and its child components in IB. They are wired to a controller. To create 170 views on the iPhone takes about 30 seconds. Eventually, I'll have over 1000 views. Besides being slow, it will also kill the app because of memory use. How much faster and memory efficient is creating everything programmatically without views? How might some of the 6000 fact type apps be doing it?
Is there a better approach instead of creating everything at once? The user may access a view in slot # 400 and begin scrolling from there. Any suggestions are appreciated.
UIViewControllers are lazy. They only load when requested, and automatically unload when memory is tight (and it's easy to unload them manually by calling self.view=nil).
"Load" here means "read a NIB," or "build programmatically." ViewControllers don't really care. Building programmatically can be a little faster since you don't have to hit the disk, but this is seldom the bottleneck, since you only display one view controller at a time.
As to which way to go, it's more style than performance (with the exception of UITableViewCells, which there are reasons you need to build programatically in most cases).
Start by studying the View Controller Programming Guide. It'll show you how iPhone intends you to do this.
To eJames' comment about NIBs being XML files, this may be slightly misleading. NIBs are binary files generated by compiling XIB files which are XML. I'd do actually profiling on the phone before assuming that NIB load time is actually a problem. Much as I am by nature inclined to programatic layout, I've found in practice that NIBs greatly simplify many UI issues in practice, and I always come back to them for large projects.
It is difficult to suggest an answer without knowing a little bit more about your specific problem, but I would venture to say that if you expect to display 1000 different things, creating 1000 individual views in IB is not the way to go.
If your pages share a common layout, you can use a UITableView to display the content of each page, and store only the data for each page in your NSMutableArray.
An excellent tutorial on how to use UITableView can be found here.
If they do not share a common layout, laying things out programmatically will be the way to go. This should be no more memory or processor intensive than doing it using IB, and in fact it will probably be faster, since it removes the need to read and parse an XML file (which is what .NIB files actually are).