Allocating and releasing of UI controllers - iphone

I am new to iOS programming and I have read some useful articles on the release and allocation of memory and thought that i understand the concept. But during the actual coding, I can't seem to really apply the theory. I am really confuse over the fundamentals of iOS programming and hope that someone can help out. Thanks in advance!
1st behaviour - Some of the apps i see can maintain the current windows state when the iPhone home button is pressed so that when the app is launch next time, it will display the last state it is in.
2nd behaviour - Some other apps will behave like its a new startup everytime it is launch. Previously displayed text, images...etc will be cleared and it will always start at the first page.
What I wanted to do is like the 2nd behaviour - clear everything when the home button is pressed and start afresh everytime it is launch.
I have created a tab bar project. The codes below will result in the 1st behaviour.
I have tried releasing all the tab controllers at applicationDidEnterBackground instead at dealloc but it didn't work. It will still display the last screen.
My questions...
1) If i call release, shouldn't it destroy the window as well? eg. [NavController1 release]. It seems that the window can still work as usual...
2) What should I change to result in the 2nd behaviour?
3) If i assign tabBarController.viewControllers = nil, does it mean that the memory for the pages attached to it previously will be release?
4) Do i need to release IBOutlets variables? eg. UIWindow *window, UITabBarController, UITextField...etc. It didn't seem to give me memory leak even if i don't release them. The reason of this might be because I don't know how to kill the app. I tried to do the double click on the iphone home button to end the app but i still not able to get the debug to reach the dealloc function. An article says that if you alloc or retain it, you should release it. Since i'm not allocating anything here, can i not release it? Or is it possible to set autorelease?
#interface MyAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate> {
UIWindow *window;
UITabBarController *tabBarController;
UINavigationController *NavController1;
UINavigationController *NavController2;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NavController1 = [[UINavigationController alloc] init];
NavController2 = [[UINavigationController alloc] init];
//This view controller inherits UIViewController and has a xib
FirstViewController *firstpage = [[FirstViewController alloc] init];
firstpage.title = #"First Page";
//These view controllers inherits UIViewController and delegate of UINavigationControllerDelegate and has a xib
SecondViewController *secondpage = [[SecondViewController alloc] init];
secondpage.title = #"Second Page";
[NavController1 pushViewController:secondpage animated:YES];
[secondpage release];
ThirdViewController *thirdpage = [[ThirdViewController alloc] init];
thirdpage.title = #"Third Page";
[NavController2 pushViewController:thirdpage animated:YES];
[thirdpage release];
// Add them as children of the tab bar controller
tabBarController.viewControllers = [NSArray arrayWithObjects:firstpage, NavController1, NavController2, nil];
// Don't forget memory management
[firstpage release];
// Add the tab bar controller's view to the window and display.
[window addSubview:tabBarController.view];
[window makeKeyAndVisible];
return YES;
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, called instead of applicationWillTerminate: when the user quits.
*/
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
/*
Called as part of transition from the background to the inactive state: here you can undo many of the changes made on entering the background.
*/
}
- (void)dealloc {
[NavController1 release];
[NavController2 release];
[tabBarController release];
[window release];
[super dealloc];
}

1) As long as the retain count is 1 your view controller will be released by calling release.
In your code, dealloc will never be called, and therefore your view controllers will not be released anyway. You need to release everything in applicationDidEnterBackground...
2) in applicationDidEnterBackground do something like:
[NavController1 release];
NavController1 = nil;
[NavController2 release];
NavController2 = nil;
tabBarController.viewControllers = nil;
then recreate everything again in
(void)applicationWillEnterForeground:(UIApplication *)application
3) doing this will release firstpage, NavController1 and NavController2 (and any other view controllers you may have added)
4) yes you should. You do not get a memory leak in this instance because the delegate never gets dealloc'd and therefore there is still a valid reference to the objects. No you cannot autorelease as autorelease will release the object when the call stack returns to the run loop.

Related

Memory leak in applicationWillEnterForeground

I am having around 9 ViewControllers in my application. I am opening the ViewControllers using the presentModalViewController. I want to display the 1st ViewController every time the user enters background and comes to foreground. If the user enters background in 8th ViewController and opens the application again , I need to display the 1st View Controller.
I am using the following code in the applicationWillEnterForeground
- (void)applicationWillEnterForeground:(UIApplication *)application
{
self.HomeScreenViewController = [[HomeScreenViewController alloc] initWithNibName:#"HomeScreenViewController" bundle:nil];
self.window.rootViewController = _homeScreenViewController;
[self.window makeKeyAndVisible];
}
and it is showing a memory leak. Obviously it will show the memory leak since I am initializing the HomeCtrl again, but I don't know how to fix it. Can anyone help me ??
I am not using the UINavigationController since the ViewControllers are pushed from right or Left side.
Change:
self.HomeScreenViewController = [[HomeScreenViewController alloc] ....];
to
_HomeScreenViewController = [[HomeScreenViewController alloc] ....];
Or you can do something like follow code:
HomeScreenViewController *tempHSVC = [[HomeScreenViewController alloc] ....];
self.HomeScreenViewController = tempHSVC;
[tempHSVC release];
self.window.rootViewController = self.HomeScreenViewController //or use just _homeScreenViewController;
[self.window makeKeyAndVisible];
And read some articles about memory management in objc.
I assume HomeScreenViewController property is declared as
#property (nonatomic, retain) HomeScreenViewController *HomeScreenViewController
Default setter method for retained properties will retain the object for you. Add autorelease to alloc init:
self.HomeScreenViewController = [[[HomeScreenViewController alloc] initWithNibName:#"HomeScreenViewController" bundle:nil] autorelease];
See also: Advanced Memory Management Programming Guide

Navigation to another view in iphone

here is my code , i am trying to get from one view to another without any memory leaks.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
firstviewcontroller *first = [[firstviewcontroller alloc] init];
[window addSubview:first.view];
[self.window makeKeyAndVisible];
return YES;
}
-(IBAction)gotosecondview:(id)sender
{
secondviewcontroller *second = [[secondviewcontroller alloc] init];
[self.view addSubview:second.view];
[second release];
}
-(IBAction)gotofirstview:(id)sender
{
[self.view removeFromSuperview];
}
to make the above code work without crashing , all i have to do is remove [second release].
if I remove it I get memory errors (build and analyze) . how can i solve this problem. and i dont want to use [self.navigationController pushViewController:second animated:YES];
all i am trying to do i navigating from one view to another and vice versa WITHOUT using navigation controller. my firstviewcontroller and secondviewcontroller are of type UIViewController.
Thanks in advance.
You need to keep the current view controller alive while its view is showing (so it can process the user input, etc.).
In your code, you can achieve that in several ways:
Keep an instance of firstviewcontroller and secondviewcontroller as instance variables, and release them on the dealloc method.
Keep an instance variable with the currently in use UIViewController and release it when you switch to another view.
The code for the second option would look something like this:
#interface
UIViewController *currentViewController;
#end
#implementation
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions {
firstviewcontroller *first = [[[firstviewcontroller alloc] init] autorelease];
[self switchToViewController:first];
[self.window makeKeyAndVisible];
return YES;
}
- (void)switchToViewController:(UIViewController *)aViewController {
[currentViewController.view removeFromSuperview];
[currentViewController release];
currentViewController = [aViewController retain];
[self.window addSubview:currentViewController.view];
}
-(IBAction)gotosecondview:(id)sender {
[self switchToViewController:[[[secondviewcontroller alloc] init] autorelease]];
}
#end
Here, all the logic for maintaining a single UIViewController alive lies in the switchToViewController method, which also handles the logic for switching from one view to another. As an added bonus, you can quickly add support for animations by adding a couple of lines in switchToViewController.
You can not release view in the call.
There is only one thing you can do in such conditions. use Autorelease,
The reason [second release] is crashing your code is likely because you're releasing your view controller which in turn releases the second view. The iPhone cookbook has some sample code on switching/swapping views if that's all that you're trying to accomplish. Here's the link. Hope this helps!
link text

presenting a modal view over a splitView

I'm having a problem making a universal app... In the application delegate I set up the main navigation for ipad and iphone:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
window = [[UIWindow alloc] initWithFrame:[ [UIScreen mainScreen] bounds]];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// The device is an iPad running iPhone 3.2 or later.
[self putSplitView];
} else {
[self putTabBar];
}
[window makeKeyAndVisible];
return YES;
}
- (void)putSplitView {
RootiPadViewController *rootiPadViewController = [[RootiPadViewController alloc] init];
UISplitViewController *splitController = [[UISplitViewController alloc] init];
splitController.viewControllers = [NSArray
arrayWithObjects:rootiPadViewController.seccionesView,
rootiPadViewController.noticiasView,
nil];
[window addSubview:splitController.view];
}
- (void)putTabBar {
TabBarController *tabBar = [[TabBarController alloc] init];
[window addSubview:tabBar.view];
}
RootiPadViewController is in charge of loading data an generating the panes for the splitView, so that its initialization incorporates a modalView with a loader, such that:
#implementation RootiPadViewController
#synthesize seccionesView, noticiasView;
- (id)init {
if ((self = [super init])) {
SeccionesVC_iPad *sec = [[SeccionesVC_iPad alloc] init];
NoticiasVC_iPad *not = [[NoticiasVC_iPad alloc] init];
self.noticiasView = not;
self.seccionesView = sec;
Init *initVC = [[Init alloc] init];
[self presentModalViewController:initVC animated:YES];
}
return self;
}
The code compiles without warnings, but for some reason the loadView method of initVC is never called and the modal view doesn't appear...
Any ideas why this might be happening?
thanks for your help!
Antonio
I know this is last year's question, but while I think I found the cause, I'm still trying to figure out a good non-hack solution for dealing with this.
Assuming you're working for iPad:
Upon initialization, splitViewController Master's viewDidLoad is called first, then the Detail's viewDidLoad.
If you attempt to put a modal window initializer in the Master's viewDidLoad, nothing seems to happen: This is because the Detail's viewDidLoad has executed after your modal view's viewDidLoad, resulting in your modal view being hidden away.
Breakpointing the modal view class, I see it enter initWithNibName in the modal view. I don't use ARC and here I assume your modal view doesn't dealloc until the program quits - The modal view is still there, but there's no way to get to it.
In iPhone:
Disregard this - because there is NO splitViewController for the iPhone. In fact, the default Universal splitViewController project treats the Master and Detail view as separate pages. So at application init, all you have to worry about is the Master viewDidLoad.
This is why, in a Universal project, pushing a modal view in viewDidLoad works as intended in the iPhone emulator, but NEVER in the iPad emulator.
Note that this only applies during the viewDidLoad stage; if you push a modal view AFTER viewDidLoad into the Master window, the modal view works as intended.
Maybe you should present your modal ViewController from the RootViewController 'ViewDidLoad' method, instead of the 'init' method... Not sure the view hierarchy is created that soon

UIViewController memory management

Hi I have a very basic issue of memory management with my UIViewController (or any other object that I create);
The problem is that in Instruments my Object allocation graph is always rising even though I am calling release on then assigning them nil.
I have 2 UIViewController sub-classes each initializing with a NIB;
I add the first ViewController to the main window like [window addSubView:first.view];
Then in my first ViewController nib file I have a Button which loads the second ViewController like :
-(IBAction)loadSecondView{
if(second!=nil){ //second is set as an iVar and #property (nonatomic, retain)ViewController2* second;
[second release];
second=nil;
}
second=[[ViewController2 alloc]initWithNibName:#"ViewController2" bundle:nil];
[self.view addSubView:second.view];
}
In my (second) ViewController2 i have a button with an action method
-(IBAction) removeSecond{
[self.view removeFromSuperView];
}
Please let me know if the above scheme works in a managed way for memory...?
In Instruments It does not show release of any allocation and keeps the bar status graph keeps on rising.
First of all, why use this scheme when second is a property:
if(second!=nil){
[second release];
second=nil;
}
second=[[ViewController2* second]initWithNibName:#"ViewController2" bundle:nil];
A propery automatically releases it's old value when the setter is used. So this could be rewritten as:
if(self.second == nil) { //Prevents creating a new controller if there already is one.
self.second = [[[ViewController2 alloc] initWithNibName:#"ViewController2" bundle:nil] autorelease];
}
Also, what's up with [ViewController2* second]?
Why is that asterisk there, and what does the class method second do?

Removing UITabBarController

Someone has posted a similar question to this with no resolution, but also no sample code. So, I thought I would post my problem in detail here.
I have a game with several modes of play, each of these having several options. After playing around with various designs, it seems cleanest to put them into a UITabBarController with three tabs, one for each class of games. I created a new UIVIewController which is loaded from the main menu screen (replacing the main screen) and initializes the UITabBarController as follows:
barController = [[UITabBarController alloc] init];
Games1 *vc1 = [[[Games1 alloc] initWithNibName:#"Games1" bundle:nil] autorelease];
Games2 *vc2 = [[[Games2 alloc] initWithNibName:#"Games2" bundle:nil] autorelease];
Games3 *vc3 = [[[Games3 alloc] initWithNibName:#"Games3" bundle:nil] autorelease];
NSArray* controllers = [NSArray arrayWithObjects:vc3, vc1, vc2, nil];
barController.viewControllers = controllers;
[self.view addSubview:barController.view];
When the user selects a game, I remove the UIViewController from the window and deallocate as follows:
- (void)dealloc {
printf("Games released: barController: %d\n", [barController retainCount]);
[barController.view removeFromSuperview];
barController.viewControllers = 0;
[barController release];
barController = 0;
[super dealloc];
}
The problem I have is that when I rotate the device, I get a crash. If I launch a game mode directly from the main screen and rotate, no crash. I've verified that everything is getting deallocated, and my retain count on the bar controller is 1. Any suggestions on how to eliminate this crash? Thanks!
[Edit] A bit more info:
The barController is defined as:
IBOutlet UITabBarController *barController;
with:
#property (nonatomic, retain) IBOutlet UITabBarController *barController;
It ends up the problem was only peripherally related to the UITabBarController. I was adding and removing UIViewControllers directly to my app window, which has been shown to be causing problems elsewhere. Adding a master UIViewController / UIView and only adding and removing from that fixes everything, although an autorelease instead of a release may have worked as well. See the discussion here:
View Controller being sent a message even though it has been deallocated
The UITabBarController was just causing the problem to happen much more quickly and obviously.
Don't do this:
barController.viewControllers = 0;
In -dealloc you should only remove the UITabBarController's view from its superview and release it.