内存管理使用的基本模式,引用计数,它的运行环境是由NSObject协议(协议声明程序接口,采用这个协议的类需要实现这个接口)和一个标准方法命名约定提供的。NSObject类同时也定义了一个dealloc方法,这个方法会在对象被释放的时候自动调用。本文将讨论那些在Cocoa程序的内存管理中你必须要了解的基本规则,并会提供一些正确使用的实例。
基本内存管理规则
内存管理的模式是建立在对象的依赖关系上的。任何一个对象都可能被一个或多个对象拥有。当对象被至少一个对象拥有时,这个对象就是存在的。如果一个对象不被任何对象拥有,那么系统将会自动地将这个对象回收。为了确保明确地了解什么时候拥有一个对象什么时候没有,Cocoa定义了如下规则:
-
当你创建对象时,你拥有它
当你使用开头为“alloc”、“new”、“copy”或 “mutableCopy” 之类的方法(例如,alloc, newObject 或者mutableCopy)创建一个对象时。 -
你可以使用retain来保持拥有状态
当方法接收到一个对象,或者这个方法同时安全地将对象返回给调用者,这个对象通常是有效的。在以下两种情况下,你需要使用retain:(1)在实现一个访问方法或init方法时,你需要保持当前的对象作为一个属性值;或者(2)避免某些操作的副作用导致对象不可用(具体描述参考避免导致你正在使用的对象被释放)。 -
当你不再需要某个对象时,你必须放弃对对象的拥有权
你可以通过给对象发送一个release消息或autorelease消息来放弃对象的拥有权。在Cocoa的术语中,放弃一个对象的拥有权被称作“释放”一个对象。 -
你绝不能放弃一个你没有拥有的对象的拥有权
根据上方的规则,这条规则是必然的。
一个简单的实例
为了说明这些规则,研究如下代码片段:
{ Person *aPerson = [[Person alloc] init]; NSString *name = aPerson.fullName; [aPerson release]; }
Person对象使用alloc方法被创建,所以当不在需要使用它的时候,我们随后给它发送了release消息。person对象的name属性没有被任何具有拥有性质的函数获取,所以它没有被发送release消息。需要注意的是,这个例子使用的是release而不是autorelease。
使用autorelease发送一个延迟释放消息
在你需要发送一个延迟的释放消息时,你可以使用autorelease——典型场景是从方法中返回一个对象。例如,你会这样来实现fullName方法:
- (NSString *)fullName { NSString *string = [[[NSString alloc] initWithFormat:@"%@ %@", self.firstName, self.lastName] autorelease]; return string; }
根据基本规则,你并不拥有stringWithFormat:返回的字符串,所以你可以安全地从这个方法中返回这个字符串。
相比之下,下边的这种实现是错误的:
- (NSString *)fullName { NSString *string = [[NSString alloc] initWithFormat:@"%@ %@", self.firstName, self.lastName]; return string; }
根据命名惯例,没有任何迹象表明fullName的调用者需要拥有返回的字符串。调用者没有义务释放返回的字符串,这将导致内存泄漏。
你并不拥有返回的引用对象
Cocoa中的一些方法生命了对象将以引用的方式返回(它们具有类型为ClassName **或id *的返回值)。一个典型的模式是当异常出现时,使用一个包含了异常信息的NSError对象,例如initWithContentsOfURL:options:error:(NSData)和initWithContentsOfFile:encoding:error:(NSString)。
着这些情况下,同样的规则适用于已经被讨论过的情况。当你调用这种方法的时候,你并没有新建NSError对象,所以你并不拥有它。所以你没必要释放他。举例如下:
NSString *fileName = <#Get a file name#>; NSError *error; NSString *string = [[NSString alloc] initWithContentsOfFile:fileName encoding:NSUTF8StringEncoding error:&error]; if (string == nil) { // 解决异常…… } [string release];
实现dealloc方法来放弃对象的拥有权
NSObject类定义了一个方法,dealloc,在当一个对象没有任何拥有者并且内存被回收——在Cocoa术语中叫做“释放”或“反分配”——时自动被调用。dealloc方法所扮演的角色是释放该对象占用的内存,并释放所有它保持的资源,包括其所拥有的对象的实例变量。
下面的实例演示了对Person类的dealloc方法的一种实现:
@interface Person : NSObject @property (retain) NSString *firstName; @property (retain) NSString *lastName; @property (assign, readonly) NSString *fullName; @end
@implementation Person
- (void)dealloc { [_firstName release]; [_lastName release]; [super dealloc]; } @end
重要提醒:绝不要直接调用其它类的dealloc方法。
你必须在实现的最后调用超类的实现。
你不应将系统资源的管理捆绑在对象的生命周期上。参考不要使用dealloc管理稀有资源。
当一个应用在终止的时候,对象也许不会发出dealloc消息。因为在推出之前,程序的内存会被自动清理,允许操作系统清理资源比调用所有的内存管理方法更简洁有效。
核心基础类库使用相似但不同的规则
在核心基础类库对象中,内存管理的机制很相似(参考核心基础类库的内存管理程序设计)。但Cocoa和核心基础类库的命名管理是不同的。具体来说,核心基础类库的创建规则(参考创建规则)并不支持返回Objective-C对象的方法。例如,下面的代码片段,你没有责任放弃对myInstance的拥有权。
MyClass *myInstance = [MyClass createInstance];
网友评论