释放对象后调用方法?
Posted
技术标签:
【中文标题】释放对象后调用方法?【英文标题】:call a method after the object is released? 【发布时间】:2012-04-04 01:26:35 【问题描述】:我正在阅读这段代码,其中setRegions
在RootViewController
被释放后被调用:我觉得有点奇怪:这是否意味着RootViewController
仍然可以访问,即使它已被释放并且self.navigationController
“拥有” “是吗?
- (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];
谢谢
【问题讨论】:
【参考方案1】:这是错误的代码。
一个对象应该保留另一个对象,只要它关心它。在这种情况下,这条规则被打破了。 rootViewController
被释放,然后如您所见,在其上调用了一个方法。这可能很危险。
在这种情况下,它可以工作。这是因为rootViewController
被传递给另一个对象,该对象保留它。所以当我们释放它时,它仍然有一个正的保留计数并且没有被释放。所以我们对它的引用仍然有效,并且调用它的方法也能正常工作。
但是假设一些实现发生了变化,initWithRootViewController:
现在由于某种原因不再保留它的论点(你不能一直做这个假设)。突然这一切都崩溃了,因为 rootViewController
被释放了。
要解决这个问题,您只需将[rootViewController release];
移动到此函数中该对象的最后一个有用引用之后。然后,您的代码会变得更加健壮和正确。
- (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];
最后要注意的是:release
和 dealloc
是非常不同的东西。 release
不一定会破坏对象。它只是将retain
计数减一。如果 retain
计数变为零,只有在之后对象才会被释放。所以这段代码有效,因为发生了release
,但没有触发dealloc
。
【讨论】:
【参考方案2】:以上是非常危险的代码。它可能会起作用,但它只是变得幸运。释放变量后,您永远不应该访问它。事实上,如果变量没有立即超出范围,最好在释放变量后立即将它们设置为nil
。有些人只在发布模式下这样做,因此创建一个宏,如:
#ifdef DEBUG
#define RELEASE(x) [x release];
#else
#define RELEASE(x) [x release]; x = nil;
#endif
这样做的原因是为了帮助在调试模式下捕获错误(通过崩溃而不是静默 nil
指针),同时在发布模式下更安全。
但无论如何,你不应该在释放变量后访问它。
【讨论】:
【参考方案3】:RootViewController *rootViewController = [[RootViewController alloc] initWithStyle:UITableViewStylePlain];
(objectA创建,retain count为1,rootViewController指向它)
UINavigationController *aNavigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
(objectB创建,retain count为1,aNavigationController指向它) (objectA的retain count现在是2,rootViewController和self.aNavigationController中的一些属性都指向它)
self.navigationController = aNavigationController;
(objectB的retain count现在是2,aNavigationController和self.navigationController都指向它;假设self.navigationController是一个retain属性)
[aNavigationController release];
(objectB的retain count现在是1,但是aNavigationController和self.navigationController都指向它)
[rootViewController release];
(objectA的retain count现在是1,但是rootViewController和self.aNavigationController中的一些属性都指向它)
[rootViewController setRegions:[Region knownRegions]];
(使用rootViewController访问objectA) (这样不好)
以下是我推荐的方式:
RootViewController *rootViewController = [[[RootViewController alloc] initWithStyle:UITableViewStylePlain] autorelease];
[rootViewController setRegions:[Region knownRegions]];
UINavigationController *aNavigationController = [[[UINavigationController alloc] initWithRootViewController:rootViewController] autorelease];
self.navigationController = aNavigationController;
【讨论】:
以上是关于释放对象后调用方法?的主要内容,如果未能解决你的问题,请参考以下文章