I have created an iPhone app where you start in a NavController and after a couple of levels you select an option from the table and an animated view pops in that has a tabbarcontroller at its root.
This is a completely seperate view that replaces the navcontroller. You tab around and when you want to go back to the options screen you press back and another animated transition plays swapping back the navigation controller removing the tab bar controller and releasing it.
The problem comes with releasing the UITabBarController. If you press the tab buttons to switch between tabs it seems you continually increase the reference count for the view controllers. Then when you call release on the tabBarController reference it will only release the view controller of the selected tab or any tabs you haven't viewed yet, and it takes one of the retain count on the others tabs viewcontrollers. This means you leak all your Model objects, custom cell objects etc from each of the other tabs that were not selected.
As an example of what I am seeing, if you create a new template TabBarController project in xcode and add a viewWillAppear method to the first view controller that prints out the retain count for itself. Start the app and press back and forth on the First and Second tab buttons and watch the log the retain count just keeps increasing.
So I am wondering if there is a way to release a tab bar controller and have it release all of its view controllers at the same time?
If this is true, it's a bug, and you should file a report on Apple Radar.
Just for completeness, I did post this problem in the apple dev forums looking for some confirmation but never heard anything back.
Checking out my app on OS3.0 shows that this behaviour is now fixed. Reference counts never go increase and releasing the UITabBarController appears to work.
Sadly I put a nasty hack in to fix this which doesn't play well with OS 3.0 so it's now conditional compilation time.
Related
I have some general question about navigation controller. I have pushed a table view with navigation controller. (now table view showing) If I click on back button of navigation controller it brings me back to main view, now the memory allocated for table view will get released automatically. Do we need to do extra?
thanks in advance.
In general, as long as you don't maintain a reference to the object, you can safely assume Cocoa Touch will do the right thing. When things aren't drawn on the screen, the APIs generally don't just hold on to them for no reason, so if you're not holding on to them either you're fine.
"Holding on" in this context either means "keeping a reference to" if you're using ARC or "not releaseing" if you aren't.
Before ARC: When you add a UIViewController to the navigation controller's stack you need to release the view controller after pushing the it onto the stack.
No need to do this if your project is ARC enabled. Read up more on ARC.
I have an iPhone application, and I have included a virtual "timeout" for being in the background. When it enters the background, I make a timestamp. When it re-enters, i compare the current time to the timestamp. This all works great.
What I want is for the application to basically reset like it was just launched. Everything in my application lives inside of a UINavigationController, so I thought I could just release it and everything inside, then reallocate it and start over. Is there a right way to do this? I have a feeling that if i just "release" the UINavigationController, all of the ViewControllers inside will just leak into memory.
You can just release your UINavigationController, and if your ViewControllers are only retained by your UINavigationController, which should be the case, then they will also get deallocated.
You may reset your model data manually and then return to start screen of your app by
[self.navigationController popToRootViewControllerAnimated:NO];
To do this in a sensible way you need to reset your controllers and data model to the state you want (e.g. as if your app was just launched). I'd also do this in a way that looks natural for your users. Something like the following:
Load a splash screen view (or something temporary) in the main app window.
Release your navigation controller (more generally, release the top level controller owning the view previously added to the window). this will release all associated controllers and views assuming you haven't retained them anywhere else.
You may also need to handle any modal views that were displayed when your app went into the background. You can either dismiss them automatically when the app goes into the background or keep state around to dismiss them when the app resumes and you are resetting it).
Reset your data model to the state you want
Recreate your navigation controller and add it back to the window and release the temporary splash screen.
I'm currently programming an app for iphone that uses the tab bar. One of the views it links to uses a navigation controller so that i can drill down the table view that I am using to display info to the user. It all works OK I can drill down the table view no problem, i can push the current table view off the view stack and return back up the stack to the first view using the back button in the navigation bar.
The problem I have is that if I drill down more than 1 view level and press the tab bar button for that view the application exits and Xcode shows a EXC_BAD_ACCESS.
The tab bars button is obviously trying to jump back to the first view in the stack, but should it be doing this?
If so, how do I make the button push everything from the stack or is it possible to disable the tab bar button from that view trying to show the top view again?
Its not really desirable for the whole app to return to the first view if the user accidentally taps the button.
Any help appreciated :)
I had an autorelease set on the view object I was trying to link back to from the tab bar and hadn't noticed that I was also releasing the same object in the dealloc method also. So when I viewed the view the first time I clicked the tab it was still in memory but when coming back to the view its retain count had been set to zero removing it from memory altogether.
Solved it by removing the release cal in the dealloc method. Alternatively removing the autorelease would have done the same thing.
I'm getting strange navigation bar behaviour, for example when I hit back button the screen displayed is the previous screen, however the Navigation Bar items do change. So I'm left with screen A, but with nav bar buttons for screen B.
Could this be due to memory leaks? I do note with my app still:
This behavior seems to happen:
immediately if I trigger memory
warning via the simulator menu, or
on a device after it has been on
for a while [without being killed
and then restarted as an app].
I do have some memory leaks I'm
trying to clean up (i.e. Profiler
highlights items in "leaked blocks"
section)
Any tips on fault finding root cause of why pushing a back button would end up in a weird state? e.g. screen on previous parent view, but nav bar items don't change...
UPDATE - I have finally removed the memory leaks in my app, however I note the nav bar issue still remains. I guess this doesn't confirm the answer to my question is NO in general, but in my specific case it wasn't the memork leak...
From Apple:
The navigation controller updates the
navigation bar each time the top view
controller changes. Thus, these
changes occur each time a view
controller is pushed onto the stack or
popped from it. When you animate a
push or pop operation, the navigation
controller similarly animates the
change in navigation bar content.
Based on this, I would start by looking for a bug or misconfiguration in your view definitions. Check for any InterfaceBuilder warnings if you defined your views via NIBs. Make sure your view hierarchies are correct in both UIViewControllers. Also check for possible bugs in your view life-cycle methods: viewWillAppear:, viewWillDisappear:, etc,.
Actually, it would be nice if you could post some screenshots and/or code. Thanks!
Any view that is not currently visible and only retained by it's view controller (as a part of the view property of that view controller) will be released (along with any non retained subviews) when a memory warning occurs.
Chances are you are creating the view as a part of init, and not retaining it in the controller (simply letting the view socket hold it from releasing). One way around this is to create properties for the views you create (nonatomic, retain), and after creating them and autoreleasing, assign to those properties, don't forget to assign those properties to nil as a part of dealloc to avoid leaking. Another way is to create your custom view elements in viewDidLoad as opposed to init.
Hard to say without code example from the offending views :)
I've seen something like this happen after calling -[UINavigationController setViewControllers:]. You might try not doing any programatic manipulation of the navigation controller's view controller beyond calling -[UINavigationController pushViewController:animated:].
I have a fairly simple iPhone application that I want to have run on both the iPhone and the iPad. I'd like to just have the iPad version be a bigger version of the iPhone version, scaled up or not -- I'm working on an iPad-specific version of the app that makes better use of the interface, but wanted to make sure my existing customers have something in the meantime.
The app is a simple tab-based application, and within each tab is a navigation controller that presents a table view, each of which can drill down a couple of layers. Everything mostly works -- I have a couple of instances of views not filling the space available, but I can fix that. My biggest problem right now is that the navigation controllers universally break when I try using them. Once I drill down a level or two, I suddenly won't be able to come back up again.
Let me try to explain in more detail:
One tab starts off with a "year" table view, showing all years that have entries; if you tap a year, it pushes another table view with all months in that year that have entries; if you tap a month, it pushes another table view that shows the individual entries; if you tap an entry, it pushes a view (a UIWebView, with some extra widgets) that shows an entry's details.
Each push is done with [self.navigationController pushViewController: foo animated: YES]. The three table view controllers I mentioned above are all created from the same nib (in fact, everything pushed onto a navigation controller in any tab is loaded from the same nib). Since I know there are only up to three levels of navigation, I just allocated three identical view controllers and use one, two, or three depending on how many entries there are.
Popping these controllers off using the "back" button seems to universally mess up the state of the view controller. So, if I drill down to the third view -- showing everything in a single month -- I won't be able to pop all the way back up to the first view: the back button stops working if I pop up one level.
Another example in another tab: it's one table view that you can tap any entry on, and a new view controller will be pushed that shows the details of that entry. If I tap an entry, tap the back button, then tap the entry again, no back button appears, or sometimes, the text that would go inside the back button appears, but no button appears!
This behavior happens if I try running the iPhone app in scaled mode (built with base SDK 4.0, supporting OS 3.2) or in native mode (built with base SDK 3.2). It runs without problems on the iPhone. I'm kind of at a loss here, because this stuff always "just worked" out of the box, mostly from the defaults that were set up back when I first created the project.
My programmer's instincts are telling me that I'm doing something very wrong in my view navigation, and that the iPad is just exposing it, but I can't figure out what it is.
Has anyone run into an issue like this, or suggest something it might be or some better way to debug what's going on?
The issue was that I had a category on NSMutableArray, which added stack-like methods push and pop. Removing this category class fixed this issue.
WTF, though.