Why doesn't this released view controller cause a crash when messaged - iphone

- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Create the navigation and view controllers
RootViewController *rootViewController = [[RootViewController alloc]
initWithStyle:UITableViewStylePlain];
UINavigationController *aNavigationController = [[UINavigationController alloc]
initWithRootViewController:rootViewController];
self.navigationController = aNavigationController;
[aNavigationController release];
[rootViewController release];
[rootViewController setRegions:[Region knownRegions]];
// Configure and display the window
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
}
Here, in the above code the reference 'rootViewController' is used to send the message 'setRegions:', even after the object has been released in the previous line.
if it's wrong then how the simulator is running without any crash?
or
if it's right then, again how?, I couldn't see a difference between autorelease & release.
SOURCE:- http://developer.apple.com/iphone/library/samplecode/TableViewSuite/listing12.html
TO DOWNLOAD:- developer.apple.com/iphone/library/samplecode/TableViewSuite/index.html

The object held by rootViewController was retained by aNavigationController so its retain count is 2, and aNavigationController was retained when it is assigned to self.navigationController so its retain count is 2. So when you release rootViewController and aNavigationController, their retain counts drop to 1 each, so they are not collected, so you can still access them through their references.
Edit
Objects are only collected once their retain counts hit 0, and any reference to the objects is still valid, (even if the reference was released) until such time. Granted normally you don't want to rely on this and should make the call prior to releasing the object, but in this case it does work.

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

pushViewController is not working

I'm fairly new to Objective-C and iPhone programming so I apologize if this is a newbie question. I have a simple application that needs to go from one view, to another. The first view is a UIViewController. I set up the xib file in IB (i.e. dragged some buttons onto the window) and hooked up all the buttons (which all work). I then created another xib file and class (also a UIViewController) and hooked them up. When a button is pressed in the first view I want to load the second view. Here's the code that is supposed to be pushing the view:
-(IBAction)createAccount:(id)sender{
CreateAccountViewController*acctView = [[CreateAccountViewController alloc] initWithNibName:#"CreateAccount" bundle:nil];
[self.navigationController pushViewController:acctView animated:YES];
[acctView release];
}
But this does nothing. When I put print statements in the createAccount method those are printed (I can click the button any number of times and it never crashes) but the acctView is never pushed. When I print out the value of self.navigationController it returns null. It's even stranger because if I present the acctView modally then it works.
-(IBAction)createAccount:(id)sender{
CreateAccountViewController*acctView = [[CreateAccountViewController alloc] initWithNibName:#"CreateAccount" bundle:nil];
[self presentModalViewController:acctView animated:YES];
[acctView release];
}
This works just fine, but I don't want to use the view modally. I'm completely lost here. In the past couple of hours I've come across a lot of posts saying to do something with a UINavigationController and hook that up to my view, but how do I do that? Any help is greatly appreciated! Thanks.
It seems you haven't created a UINavigationController for your app.
Best thing would be starting from scratch with a new Xcode project, taking care of choosing a Navigation Based application. In this way you will get almost everything already set up for you.
If you don't like this approach, you can create programmatically your UINavigationController. Here you find a tutorial for doing that.
If you prefer more straight-to-the-point instructions, here they are:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
MainPageDialog *overviewViewController = [[MainPageDialog alloc] initWithNibName:#"MainPage" bundle:nil];
self.navigation = [[[UINavigationController alloc] initWithRootController:overviewViewController] autorelease];
[overviewViewController release];
[window addSubview:[navigation view]];
[self.window makeKeyAndVisible];
return YES;
}
whereby self.navigation is a retained property in your appDelegate.
EDIT:
This answer was quite old, therefore an update:
If you are using ARC, you should be using a strong (vs. retain) property and you would not need the autorelease;
if you target iOS > 4.0 (which is also implied by the above point), you can use the rootViewController property in UIWindow and say:
MainPageDialog *overviewViewController = [[MainPageDialog alloc] initWithNibName:#"MainPage" bundle:nil];
self.window.rootViewController = [[[UINavigationController alloc] initWithRootController:overviewViewController] autorelease];
[overviewViewController release];
[window addSubview:[self.window.rootViewController view]];
[self.window makeKeyAndVisible];
without the need for any navigation property.

iPhone - Loading a new view and a deallocated object

So I feel like a serious rookie right now, but I have a problem I can't seem to figure out. I have a barebones app, with literally nothing in it except a login screen and a second view containing a tableview. When I add the second view after logging in (I have done this like 4 times before...), the table view goes through its delegates and appears that it's going to load, but something happens. I have enabled my NSZombies, and it appears to be deallocating the new view, right before it appears.
After tracing through it, and building up again piece by piece, it appears to happen after I wire the table to the view as the datasource/delegate in IB. I have set the view as a UITableViewDelegate, and the methods indeed get fired. Does anyone have any idea what might be causing this behavior?
Have you added the 'second'view to an exisitng view using addSubview: or added it to some form of UINavigationController or UITabBarController? When you do this it will automatically increase the retain count and whatever code you have releasing the view won't cause is to be deallocated.
In my AppDelegate application:didFinishLaunchingWithOptions I have something like;
LoginViewController *login = [[LoginViewController alloc] init];
[login setDelegate:self];
loginNavController = [[UINavigationController alloc]
initWithRootViewController:login];
[window addSubview:[loginNavController view]];
And then once login has occured (and succeeded using a protocol/delegate to send the message back to AppDelegate) I call this code;
UIViewController *newView1 = [[UIViewController alloc] init];
UIViewController *newView2 = [[UIViewController alloc] init];
UIViewController *newView3 = [[UIViewController alloc] init];
myTabBarController = [[UITabBarController alloc] init];
myNavController = [[UINavigationController alloc]
initWithRootViewController:newView1];
// nav controller now retaining
[newView1 release];
NSArray *viewControllers = [NSArray arrayWithObjects:myNavController,
newView2,
newView3,
nil];
[myTabBarController setViewControllers:viewControllers animated:YES];
[[myTabBarController view] setFrame:[[UIScreen mainScreen] applicationFrame]];
[window addSubview:[tabBarController view]];
// tab bar controller now retaining
[newView2 release];
[newView3 release];
// remove login from application
[[loginNavController view] removeFromSuperview];
The AppDelegate has the following declared in the header file;
LoginViewController *loginViewController;
UITabBarController *myTabBarController;
UINavigationController *myNavController;
In the dealloc method for the AppDelegate these are released.
This gives me my login page and then when that has processed my views with a top nav all controlled using the bottom tab bar.
Hope this helps in some way.
You have either too many release (or autorelease) calls - or not enough retain calls - in your view loading/transitioning code, but it's impossible to be more specific without seeing that code.
What's probably happening is the autorelease pool is being flushed between your view loading and your view being shown, and that's what's leading the behaviour you describe.

Bad memory leak when creating a view - even though a 'release' is present?

Part of my code presents a UITableViewController in the following way:
FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:#"Settings" bundle:nil];
flipside = [[UINavigationController alloc] initWithRootViewController:controller];
controller.delegate = self;
flipside.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:flipside animated:YES];
[flipside release];
[controller release];
Using the leaks tools, no memory leaks are picked up on. However, whenever I bring up the settings menu (as shown in the code above), more memory appears to be allocated and never released - almost 100 kB every time.
Weirdly, the inclusion of the two release statements at the end seems to have no effect on the memory allocation..? Is there something I am misunderstanding about memory allocation in objective-c, or is something weird going on?
Any ideas are much appreciated - thanks!
If flipside is a retained property then the navigation controller is leaking. the problem is that you are bypassing the accessor method and releasing flipside directly. This is just messy code. A better way to do it would be to make an accessor method for flipside that will only alloc a new one if you haven't already created one. It's called lazy loading. To do this, just leave the #synthesize for flipside (but you shouldn't set it from outside the accessor method), in your header file change the property to, and add this method to the implementation:
- (UINavigationController *)flipside {
if (flipside != nil) {
return flipside;
}
FlipsideViewController *controller = [[[[FlipsideViewController alloc] initWithNibName:#"Settings" bundle:nil];
controller.delegate = self;
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller];
[controller release];
navController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
// This implies a retain if your flipside property is set to retain
self.flipside = navController;
[navController release];
}
make sure to put self.flipside = nil in the viewDidUnload method of the view controller the code you included came from (I assume it's a presentSettings action).
what your presentSetting action should now look like is this:
- (IBAction)presentSettings {
// make sure you use the accessor self.flipside instead on accessing the variable directly
[self presentModalViewController:self.flipside animated:YES];
}

Allocating and releasing of UI controllers

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.