How does UINavigationController keep track of multiple UIViewControllers? - iphone

I have developed an application that uses UINavigationController to handle multiple instances of the same UIViewController, and it is working great. The UIViewControllers that I am using, however, are using a lot of images in a sometimes very big UITableView as loaded from the server, so I am beginning to be more conscience of performance and memory.
What happens to a UIViewController that isn't at the top of the stack? If I drill down through a few views, that all have images, whenever I return to the view, the UINavigationController presents the view without any loading, so I'm wondering if those views on the stack are in any way potentially hogging lots of valuable resources? Such as memory?
What is the best way to handle that situation? Is there anything I should know about that I apparently don't?
Thanks!

This is, actually, explained in the documentation for iOS. Check XCode>Organiser>Documentation>iOS, there is a nice drawing explaining how the UINavigationController stack works, that should be better than words.
However, in a nutshell, you have viewDidLoad and viewWillAppear (and their opposites) methods that serve to alleviate your fears. They should be used to create/delete any "memory hogging" parts of your code that you don't need "right now". However, as far as I know, you don't have control over what iOS will decide is necessary to flush or not, if it's controlled directly by UINavigationController.

Related

Alternative way to load ViewControllers on a UIPageControl

In my project I'm using a UIPageControl as a container UIViewController for scrolling. I used this tutorial (using ARC and storyboards): http://www.wannabegeek.com/?p=168 and the source code: https://github.com/wannabegeek/PageViewController
As you can see there are 3 ViewControllers in the project, and they are added as child on the CustomPagerViewController. In that project there are only 3 ViewControllers that are added, but in my project I got more than 3 ViewControllers and I also reuse them with another text, image, label, etc. on it. The problem is that in the project all those ViewControllers are getting loaded whenever the CustomPagerViewController is loaded and this costs memory so I'm looking for another way how I can deal with this problem instead of loading them all at once?
You shouldn't try to optimise controller allocation in this way. Obviously retaining view controllers in memory takes up space, but believe me, not that much.
We have to difference here between the controller and its view. Views can take up a lot of memory in order to get themselves displayed on screen, but iOS already has its own mechanisms to free this memory when it's no longer required (ie: the view is not on screen / doesn't have a window).
iOS view controllers used to free views memory by themselves (this behaviour was in [UIViewControllers didReceiveMemoryWarning]), but that's no longer the case in iOS 6. Now you're responsible for doing so in you feel like it's needed by your app. Bear in mind that despite nilling these views in this method (or dealloc), you won't be saving much memory, as most part of the (graphical) resources used to display a view in screen may have already been released by iOS, and the amount of memory you may end up freeing close to 0.
To sum up, in your case I'd convert PagerViewController into a proper iOS container controller using this guide. The key is to call the following methods:
[UIViewController addChildViewController:]
[UIViewController willMoveToParentViewController]
[UIViewController removeFromParentViewController]
[UIView addSubview:]
[UIView removeFromSuperview]
in the right order according to your needs. In your case, you can add / remove these in the scrollViewDidScroll method. Use them in this way, and let Apple's magic happen.
You might as well use UIPageViewController which gives you some nice out-of-the-box features.

Poor animation performance transitioning to UIViewController (via UINavigationController) and not sure why

I have a simple UIViewController whose view is created via a Nib. Here's the structure of the Nib:
And a screenshot of the layout:
Whatever the previous view (there are 2 possibilities), there is significant stutter/lag when transitioning to this view. Even the keyboard animation is lagged. Also, this is only on an actual device.
I've tried removing the MKMapView to see if that was the case, but it didn't make a difference.
Is the Nib too complex? Should I load everything via code? I'm not sure what it could be, but its really annoying, especially when the rest of the app is super crisp.
As far as code goes, its nothing special: just alloc/init a view, push it onto a UINavigationController, etc. Nothing in the viewWillAppear:/viewWillDisappear:.
Coming from Reddit, where you posted code (probably want to do that here too):
My guess would be the lag is coming from the section from line 44 through 80ish, where you build the overlay. Something in there might be taking longer than you think (Spotpoint unSerialize sounds fishy). You typically want to do as little work as possible in viewDidLoad, since that is called as the UI is transitioning to the new view controller.
Try throwing that code into a GCD queue or running it in the background, and see if that helps the loading stutter.

good examples of model-view-controller

I'm new to Objective-C and the iPhone and thought I was getting the hang of it until, after many play apps, I ran into a really basic problem around MVCs, NIBs and IB. Are there any really clear explained examples of how to follow this framework that I could go back to?
#interface test1ViewController : UIViewController {
IBOutlet myView *myview;
IBOutlet myModel *mymodel;
}
Both the views and models are linked in by IBOutlets but instantiating the model object either kills the application or produces an object which does not respond to any messages.
I am also unclear as to where to carry best out initialisations. I currently do this over viewDidLoad in the view controller. Is there a standard way to do this and does the simulator actually always start up in the same way? Should one use awakeFromNib? Does it make any difference if I use plain code or the IB? And if I have use the IB, should it include a model object?
The MVC idea would make good sense for me here because I have potentially several views and view controllers all feeding back into - and sharing - one common central data model. Any references or advance on this newbie problem would be more than welcome!
I wouldn't spend too much time worrying about the 'classic' definition of MVC. iOS follows it, but there's a lot of confusing terminology. ("View Controller")
You say trying to use model kills your app. Are you retaining myModel? You have to retain all IBOutlets.
nibs are collections of "Freeze-Dried" objects. When you load a nib, the objects in it are "rehydrated", if you will. This means they spring back to life with all of their properties set to whatever they were when you froze them. So you talk of "instantiating" and "initializing" but this does not apply to IB. The objects are ALREADY instantiated and initialized. Imagine that compiling the nib 'pauses' the objects. When you load the nib, the objects resume doing whatever they were doing when frozen. They will get an awakeFromNib message, so that's a good place to put some code to check on what the state of the app is, see if you have to do stuff with your object to bring it up to speed.
viewDidLoad seems like an "initialization" method but don't be fooled. It's part of the view controller life cycle and it can be called more than once! (If your controller's view is purged as part of a low memory warning, viewDidLoad might be called again if the view has to be... wait for it... reloaded.) So, it's appropriate to put view setup stuff in viewDidLoad, but not other initialization type things.
For the "common data" thing, I like to create a singleton data model class. Your various views can set properties on the model, or send notifications. You can also use KVO (key value observing) to watch for changes in the model.
IB makes functionality invisible. I don't like it and I don't use IB any more, preferring to have everything in code. Then when you look at code you see what is going on - all the navigation controllers, all the formatters etc. - without switching over to IB. Maybe Xcode4 will make it better with integrated IB but I probably won't go back. Lots of people do like IB so try both methods and see what you like best.
IBOutlet/IBAction actually mean nothing to the compiler but they let IB recognise the ivars it can send messages to or that will write to elements in a xib. Your use of it here is a bit simplistic unless you really do have a model that only communicates one way with the xib. What you more usually have is a range of controls in the xib linked to the view object, the view communicating directly with the controller. The controller communicates with the model. Very loosely speaking, model is your internal representation of data and logic, view is what you see, controller is the glue between them.
The MVC line can be fuzzy and you just have to get comfortable with it. If you have a slider control with a value representing some value in your model then it can be hard to think of it as part of the interface especially when you persist the value and use it as a central part of your model. I like the Stanford iPhone class introduction of it. They don't spend a heap of time on it because it can be difficult to follow exactly and there are situations where it isn't best.
Notes from that class - you can find the video on iTunes to follow along.
Your use of viewDidLoad is correct, that's your chance to perform initialization on views and their objects. If using IB you will probably not have much to do there because you can set most properties in the xib. If not using IB you will use it a lot more.
A lot of times something like your model would be wired at runtime by your application delegate or by the view controller itself.
IB is generally used more to link views and controllers together, with the application handing around model(s).
That said, you should be able to have IB create an instance of your model and assign it in an IBOutlet. Was your model OK with just being created without the classic init method being called? Did it implement NSCoding properly?

Can I load multiple UIViewControllers that each kick off their own NSURLConnections?

My Rootviewcontroller uses NSURLConnection to get data from a server, and then, based on this data, loads a bunch (like 7) of smaller UIViewControllers that each also use their own NSURLConnection to get some more specific data from the server. But, the problem is, only the RooTViewController is recieving callbacks from:
- (void)connectionDidFinishLoading:(NSURLConnection *)theConnection
the other UIViewControllers never get callbacks...
You should really work with the assumption that only one view controller is active at the time. So if you want the other view controllers to do work, even when they are not visible on the screen, then you should move that logic into some singleton object that does all the network communication. The viewcontroller, when they appear, will simply ask this object for the data.
For the iPhone it is a really bad design to let inactive view controllers do stuff in the background. The only thing they do is manage the view that you currently see on the screen.
#St3fan is correct about good patterns for UIViewController. Many a developer has been burned thinking his UIViewController always has a non-nil view (it doesn't).... Even so, I'll discuss here what may be going wrong.
There are two likely causes: you're not setting the UIViewControllers as the delegates of their NSURLConnections, or you're not actually starting the NSURLConnections. The most likely cause of the latter is that you're using NIBs and expecting them to load at the beginning of the program, when they actually only load when needed.
As #deanWombourne notes, you can have all the NSURLConnections you want (**). Walk through your code in the debugger. Make sure you're not sending messages to nil. When "nothing happens" it almost always means you're sending messages to nil.
(**) Don't get crazy with connections on iPhone. Network connections are expensive to create (in both time and battery). If you can get all the data you need over a single connection, there are some advantages to doing so. This doesn't mean you should bend over backwards to avoid connections; just be careful of letting them get out of control.

UITableViewController.view crash

So I'm trying to use a UITableViewController (let's call it homeView) in my iPhone application to display a simple table with only a few rows of text which are loaded from an NSArray in the controller. I want to display the table in the grouped style in a subview of a subview (let's call it subSubView) of my main controller. When I try the following: [subSubView addSubview:homeView.view], my app crashes on launch. However, when I allocate the object without adding it to any views, it launches fine.
What's the best way (or rather a working way) to display the table generated by my UITableViewController?
There isn't enough to tell for sure what is going on, but if I had to guess I would think that you probably aren't retaining homeView. Normally I would say that as a comment to your question, since it is not really an answer, but I have a completely separate answer:
Just use a UITableView, not a UITableViewController. Instead of trying to embed a controller within a controller (which is quite difficult since Apple doesn't expose the necessary tools to actually modify the view controller hierarchy), just make the VC you are writing support the appropriate delegate/dataSource methods and directly create the view.
While it might make some logical sense to try to embed VCs inside of each other, with the exception of the builtin container VCs (UINavigationController, UITabBarController) it Really Doesn't Work™. The technical reason for this is that internally some of the event routing and messaging depends on parentViewController being correct, but since you can't set it (setParentViewController: is private) tons of latent bugs in UIKit start rearing their head. The internal collection classes can set the parentViewController correctly, so everything works right.
Also, one last thing. In your question you named your view controller homeView. Please, please on't do that. A view controller and a view are separate things, call it homeViewController. Aside from the fact that a lot of new iPhone developers get confused about what the distinction is, there is nothing more aggravating then tracing through someone else's code and realizing that something you are assuming is one type is another.