对象的生命周期包括诞生(通过alloc或new方法实现)、生存(接收消息并执行操作)、交友(通过复合以及向方法传递参数)以及最终死去(被释放)。
生命周期结束后,它们所占用的内存将被回收并被新的对象使用。
Objective-C的对象都是在堆里面生成,然后有一个指针指向它,使用完后不会自动销毁,需要执行dealloc来销毁,否则就会出现内存泄漏:
ClassA *obj1 = [[ClassA alloc] init]; //alloc
[obj1 dealloc]; //dealloc
Cocoa采用了引用计数(Reference Counting)或称为保留计数(Retain Counting)的技术来表示对象的生命。
每个对象都有一个与之相关联的整数作为引用计数器。
当某段代码需要访问一个对象时,该代码就将该对象的引用计数器值+1,表示“我需要访问该对象”;
当某段代码结束对象访问时,该代码就将该对象的引用计数器值-1,表示“不再访问该对象”;
当对象的引用计数器值为0时,表示“不再有代码访问该对象了”,因此该对象将被销毁,其占用的内存资源将被释放。
可以发送retainCount消息来获得引用计数器的值:
- (NSUInteger) retainCount;
当使用alloc、new方法或者通过copy消息(接收到消息的对象会创建一个自身的副本)创建一个对象时,对象的引用计数器被置为1。
要增加对象引用计数器的值,可以给对象发送一条retain消息,retain消息返回的是一个id类型的值:
- (id) retain;
要减少对象的引用计数器值,可以给对象发送一条release消息:
- (oneway void) release;
执行release方法释放的是一块内存区域的内容,而不是仅仅切断对象和该内存区域的联系!
示例程序:
@interface RetainTracker : NSObject
@end // RetainTracker
@implementation RetainTracker
- (id) init
{
if (self = [super init]) {
[self retainCount]);
}
NSLog (@"init: Retain count of %d.",
return (self);
} // init
- (void) dealloc
{
NSLog (@"dealloc called. Bye Bye.");
[super dealloc];
} // dealloc
@end // RetainTracker
int main (int argc, const char *argv[])
{
RetainTracker *tracker = [RetainTracker new]; // count: 1
[tracker retain]; // count: 2
NSLog (@"%d", [tracker retainCount]);
[tracker retain]; // count: 3
NSLog (@"%d", [tracker retainCount]);
[tracker release]; // count: 2
NSLog (@"%d", [tracker retainCount]);
[tracker release]; // count: 1
NSLog (@"%d", [tracker retainCount]);
[tracker retain]; // count 2
NSLog (@"%d", [tracker retainCount]);
[tracker release]; // count 1
NSLog (@"%d", [tracker retainCount]);
[tracker release]; // count: 0, dealloc it
return (0);
} // main
当一个对象因其保留计数器归0而即将被销毁时,Objective-C会自动向对象发送一条dealloc消息。
我们可以自己在对象中重写dealloc方法,这样就能释放掉已经分配的全部相关资源。
但一定不要直接调用dealloc方法,因为Objective-C会在需要销毁对象的时候自己调用它!
如果一个对象内有指向其他对象的实例变量,则称该对象拥有这些对象。
一般来说,我们说某个实体“拥有一个对象”时,其实也意味着该实体要负责清理其拥有的对象所占用的资源。
当多个实体拥有某个特定对象时,对象的所有权将变得很复杂,这也是引用计数器的值大于1的原因。
所有权变得复杂起来,就可能出现下列情况:
ClassA *obj1 = [[ClassA alloc] init]; // count = 1
[obj1 release]; // count = 0, 自动dealloc
ClassA *obj1 = [[ClassA alloc] init]; // count = 1
ClassA *obj2 = obj1; // count = 1, 指针赋值不会增加引用计数
[obj1 hello];
[obj1 release]; // count = 0, 自动dealloc
// 因对象已被dealloc, 故以下代码不会执行, obj2仍然是无效指针:
[obj2 hello];
[obj2 release];
正确的做法是,凡是访问方法,都先保留新对象,再释放对象:
ClassA *obj1 = [[ClassA alloc] init]; // count = 1
ClassA *obj2 = obj1; // count = 1
[obj2 retain]; // count = 2,先为新对象做保留
[obj1 hello];
[obj1 release]; // count = 1
[obj2 hello];
[obj2 release]; // count = 0, 自动dealloc
按照这个规则,我们可以编写setEngine方法的内存管理版本:
- (void) setEngine: (Engine *) newEngine
{
[newEngine retain]; //保留新对象
[engine release]; //释放对象
engine = newEngine;
} // setEngine