call a method after the object is released?

Go To StackoverFlow.com

0

i was reading this code, where setRegions is called after RootViewController is released : i find it a bit strange : does it mean RootViewController is still accessible, even if it was released and self.navigationController "owns" it ?

- (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];
}

Thanks

2012-04-04 01:26
by Paul


4

This is bad code.

An object should retain another object for as long as it cares about it. And in this case that rule is broken. The rootViewController is released, and then as you note, a method is called on it. This can be dangerous.

In this case, it works. This is because rootViewController is passed to another object, which retains it. So when we release it, it still has a positive retain count and is not deallocated. So our reference to it still works, and methods called on it work fine.

But lets say some implementation changed and initWithRootViewController: now no longer retained it's argument for some reason (an assumption you can't really make all the time). Suddenly this all crashes because rootViewController gets deallocated.

To fix this funk, you just need to move [rootViewController release]; to after the last useful reference of that object in this function. Your code then becomes more robust and more correct.

- (void)applicationDidFinishLaunching:(UIApplication *)application {

    // Create the navigation and view controllers
    RootViewController *rootViewController = [[RootViewController alloc] initWithStyle:UITableViewStylePlain];
    [rootViewController setRegions:[Region knownRegions]];
    UINavigationController *aNavigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
    self.navigationController = aNavigationController;

    // Release temporary objects since we've now sent them to other other objects
    // which may or may not retain them (we don't really care which here)
    [aNavigationController release];
    [rootViewController release];

    // Configure and display the window
    [window addSubview:[navigationController view]];
    [window makeKeyAndVisible];
}

Last thing to note: release and dealloc are very different things. release does not necessarily destroy objects. It simply decrements the retain count by one. And if that retain count ever gets to zero, only then is the object is deallocated. So this code works because a release happens but without triggering a dealloc.

2012-04-04 01:33
by Alex Wayne
thanks Alex Wayne for the answe - Paul 2012-04-05 01:24


1

The above is very dangerous code. It might happen to work, but it's just getting lucky. You should never access a variable after you have released it. In fact, it is best practice to immediately set variables to nil after releasing them if they don't immediately go out of scope. Some people only do this in Release mode, and so create a macro like:

#ifdef DEBUG
#define RELEASE(x) [x release];
#else
#define RELEASE(x) [x release]; x = nil;
#endif

The reason for this is to help catch bugs in debug mode (by having a crash rather than just a silent nil pointer), while being a bit safer in release mode.

But in any case, you should never access a variable after you've released it.

2012-04-04 01:31
by Rob Napier
thanks Rob Napier for the answe - Paul 2012-04-05 01:23


1

RootViewController *rootViewController = [[RootViewController alloc] initWithStyle:UITableViewStylePlain];

(objectA created, retain count is 1, rootViewController points to it)

UINavigationController *aNavigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];

(objectB created, retain count is 1, aNavigationController points to it) (objectA retain count is 2 now, both rootViewController and some property in self.aNavigationController point to it)

self.navigationController = aNavigationController;

(objectB retain count is 2 now, both aNavigationController and self.navigationController point to it; assuming self.navigationController is a retain property)

[aNavigationController release];

(objectB retain count is 1 now, however, both aNavigationController and self.navigationController point to it)

[rootViewController release];

(objectA retain count is 1 now, however, both rootViewController and some property in self.aNavigationController point to it)

[rootViewController setRegions:[Region knownRegions]];

(use rootViewController to access objectA) (This is not good)

Following is my recommended way:

RootViewController *rootViewController = [[[RootViewController alloc] initWithStyle:UITableViewStylePlain] autorelease];
[rootViewController setRegions:[Region knownRegions]];

UINavigationController *aNavigationController = [[[UINavigationController alloc] initWithRootViewController:rootViewController] autorelease];
self.navigationController = aNavigationController;
2012-04-04 01:41
by yehnan
thanks yehnan for the answe - Paul 2012-04-05 01:20
Ads