I have an iPhone app that has a 4 option menu, and allows the user to switch between view controllers quickly. So I thought to make the experience smoother, every time the user switches between view controllers, the from view controller is released right away, and the to allocated. And the user will most likely be doing a lot of switching. Is there a better way to handle memory here than to keep releasing and allocating the same view controllers over and over again?
View controllers aren't expensive. It is ok to alloc and release them. However the views can be expensive. When memory is low, the system tries to unload the views of the view controllers which are currently not visible. Your app should always be aware of that. Release expensive objects in viewDidUnload, i.e. IBOutlets and data that can be recreated.
If you experience performance issues you should hold all 4 view controllers in memory. So the views will be loaded faster. iOS takes care of unloading the not visible views (when appropriate).
If it's the user doing the switching, time spent in dealloc and/or alloc is completely insignificant, bar any extremely time-consuming operations like loading tens/hundreds of images, etc.
In short, both an on-demand and a cached solution will have some tiny advantage over the other. Your users will not notice the difference though.
It sounds like you might have been better off with a tab bar app?
If memory is your main concern then it sounds like you will be loading and unloading a lot which will be a slower experience for the end-user.
If you use a tab bar controller then the views in unused tabs will be automagically unloaded if memory is needed. and loaded again if/when necessary.
Related
Suppose I have 2 views. In the first view, I allocate memory to displaying many UI components such as an UILabel, UIImages, etc.
Suppose the user navigates to the next view (via UINavigationController)
Is it OK to deallocate memory assigned to displaying UI components in the first view and then initialize them again once the user goes back to the first view (in viewFirstLoad or the appropriate function)?
It seems to me if you don't do this, then memory will keep on increasing the longer the user uses your app in that particular session.
Is this not allowed? frowned upon? impossible?
It is perfectly normal and in fact, that functionality is built in standard UIViewController - when controller is not displayed its view may be released from memory and you can release all its subviews (e.g. retained through IBOutlet references) in controller's -viewDidUnload method.
When controller needs to display again it reloads its view again.
It depends. Generally, the rule of thumb is that you should free objects that you don't need. If your view is just a view, then yes, I'd release it and all of its subviews. If your view has data that was obtained through a lengthy retrieval process (e.g. a web service call), I'd probably hold onto the data somewhere so that I don't have to go back out and retrieve it when the user goes back to the first view.
To clarify a little: Apple recommends you display data specific to a view in it's -viewDidLoad method, such as setting text on labels. Then, in -viewDidUnload you should release (or nil outlets of) the view objects you setup in -viewDidLoad. It's critical you implement -viewDidLoad, as the base UIViewController code checks that it's subclass actually implements -viewDidLoad before it assumes it can unload the view (and therefore call -viewDidUnload). Failing to implement -viewDidLoad results in the controller thinking it can't recreate your view at a later time, and so it doesn't unload the view from memory. A developer I know experienced this same problem, took forever to track down.
I am new to iPhone development... trying to figure out the best design for 70+ views. Do I have 1 Navcontroller and 70 views or what?
I originally thought I would have about 10 XIBs each with their own NavController and views, but I haven't found anyone that seems to think this is correct or not.
I think I understand the iPhone does not handle the memory dealloc by itself so I am assuming that will be a bit choice on how to make this work.
If you are going to implement 70+ views means you have to use both the navigation controller as well as tabbar controller. Then only the user can easily access all the views. If you are using 1 navigation controller and 70+ views means it is very difficult for the user to view all the views.
If you would like to design your views graphically using Interface Builder (built into Xcode4 or separate app for Xcode 3 and prior), I would just have a UINavigationController in your first xib (one with the UIWindow). You can then design each of your other views separately, in their own xib file and load them and push them onto the navigation stack as necessary.
Your understanding is correct; iOS does not support automatic garbage collection, although there is a pretty established paradigm of how to allocate and release memory as needed to avoid memory leaks. 70 views in an iPhone app is quite a bit, but it should be fine in theory, although I can see there being issues if the user has to drill down quite a ways in the hierarchy and each successive view eating up more memory.
First up, i realize this application has some design issues and while rewriting a huge chunk of it would probably solve my issues, I'm looking for a way to make things faster right now.
Basically I've got
viewController = [[MyViewController alloc] initWithNibName:nil bundle:nil];
viewController.view;
where MyViewController.xib is a rather complex nib file with a lot of intertwined controllers and views, some of which of course load their views and hierarchies from other nibs, but certainly not all. This takes around 7 seconds to load, which is a really long time considering this is an iPhone application.
The reason for forcing the view to load is that I'm also doing a some network requests in a parallel thread and once those have finished I'd like to set up quite a few things with these views.
My question is: how do I find out which parts of my nib are taking so long and what would be the best strategy to optimize this without completely rewriting the whole app (for now)?
The first thing to do is not to assume that the nib loading is the bottleneck without direct evidence that it is. Nib loading is highly optimized and very efficient. It is more likely that your bottleneck is the network request than the nibs. Use Instruments to get concrete data about where the app is spending its time before you do anything.
Assuming it is the nibs, Andiih answer is the best for profiling but it sounds to me like you've really got a design issue.
It sounds like you need to break your big nib up into smaller nibs.
Its common to have one view per nib. For example suppose you had an app that had a tabbar with five tabs. Then each tab had a table view which would open a detail view. In the normal setup, you would have 11 separate nibs. One nib would define the tabbar, one nib for each tab's view and then one nib per detail view. When the app started, only the nib for the tabbar and the first visible tab would load so you would only load two of the 11 nibs to start.
To optimize, the first thing to do is to break it down into the smallest possible individual nibs. A nib only loads when needed. You should place the functionality throughout your nibs such that you don't load the nib if its view is not immediately displayed.
Nibs are just lightweight data files so don't be afraid to create nibs that 99% identical. For example, I often use separate views for each portrait and landscape view. For the user's perspective, it's one view but I have three separate nibs. I do this in cases when its easy to use an entirely separate view than to rotate a single complex view. Each nib is loaded when when its orientation is used. If the orientation never changes, it's never loaded.
If you have a complex interface which changes significantly depending on a network request, it will be faster at runtime to have separate nibs for each variation than it will be to cram all the functionality into one nib.
I have no idea if this would really work, but you could put logging statements in initWithCoder in each controller which will be called as each sub-xib loads. That might give you some clues.
I guess instruments don't give you anything useful ?
Something odd is happening with my view controllers. When an applicationDidReceiveMemoryWarning is posted, it removes all views from the stack other than the visible view (a second level view) which is expected behaviour. However, if I then navigate back to the root view, it also has a back button that navigates back to itself. From there on the app views behave very oddly to the effect that the app is useless.
More strangely the exact same second-level view (with no memory leaks) can work fine without any memory warnings, yet sometimes on app launch cause a memory warning and therefore the navigation issues. The view holds all the same controls and data as before, yet can sporadically be too much for the device to deal with. Is this something to do with memory available on the device at the time of launch? This only seems to happen on hardware.
Any ideas?
I have found the problem - and it's my code after all.
On application launch I load my root view controller, then in my root view controller's viewDidLoad method I am telling it to load a second view if a condition applies.
When the memory warning occurs, it's recreating the root view controller when exiting the second view, and then creating the second view again whilst still showing the root view.
This then causes the navigation to go mental.
I am by far no expert on memory issues. But odd behaviour after this kind of things indicate to me that the removing of memory did not happen consistently. Maybe some references are still kept and the app thinks these are valid references, but in reality, the memory is no longer allocated. It could also be possible that the memory is reallocated with the old references still in place which could also lead to strange behaviours.
Hm, i can not offer much advice here, only that you could check references to unallocated areas
I have developed an iphone application that opens to a tabbed view with the first tab being a uinavigationcontroller. Within that controller is a uiviewcontroller that contains a uitableview. There are 2 items listed in the tableview. When I select one or the other item, it displays yet another uiviewcontroller with dynamically generated uiviews.
When I press the "Back" button at the top of the navigation control, to return to the previous uiviewcontroller (that contains the tableview), and then I select 1 of the 2 items in the uitableview again, it eats up almost 2M of memory according to Instruments. This occurs each time, until it reaches about 24M, and my application crashes.
I am registering no leaks whatsoever.
Is there something I need to be doing when the "Back" button is pressed to release the memory allocated to the uiviewcontroller.
I'm not sure how far you are in iPhone development, or how much you know about the memory management, but it could be a reference counting issue. Remember: If you call alloc or retain, you need to call release, and never call release on something you haven't alloc'd or retained.
The navigation controller retains all view controllers pushed onto its stack, so if you ensure that such view controllers are autoreleased or that you otherwise have no claim on them (e.g., alloc, push, release), they will automatically be released when popped.
If you're doing this and you're still losing memory, perhaps you are over-retaining the your custom views from their view controllers?
It's difficult to say without seeing code, but one thing that might be useful is implementing -didReceiveMemoryWarning on all your UIViewControllers and logging details of them -- then if you see a memory warning from a view controller you think should have been deallocated, you have a starting point for further investigation.
Also, have you tried the Clang Static Analyzer? The Leaks tool is useful, but gives plenty of false negatives. The CSA is no panacea either, but it catches some things Leaks misses.