reference count (引用计数)的概念其实很简单, 就是每个对象都有一个reference count 用来表明有多少其他的对象目前正保留一个对它的引用(reference). 对象A 想要引用对象B, A 就把B 的 reference count 加 1。 当A 结束了对B 的引用, A 就把 B 的reference count 减 1. 当没有任何对象再引用 B 时, B 的 reference count
就减为0, B 就被清除(deallocated), 内存就被释放。清除B的时候, 被B所用的对象的 reference count 也可能减小, 也可能使它们被清除。
reference count (引用计数)是 NSObject 和 NSAutoreleasePool 两个类提供的机制。引用计数不是自动的, 编译器无法决定一个对象的生命周期,所以必须手动调用以下方法来控制 引用计数.
- (NSObject *) incorrectMethod1
{
NSObject *result = [[NSObject alloc] init];
return result;
}
错误是:
没有相对应 alloc 消息的 -release . 记住 :新的NSObject 实例生成时,reference count 为1, 并且实例被返回,但如果这个实例返回后不被使用, 那末就出现了内存泄露。
另一个错误的方法:
-(NSObject *) incorrectMethod2
{
NSObject *result = [[ NSObject alloc] init];
[result release];
return result;
}
错误:
虽然参照规则, 使用了- release 去对应 +alloc , 但是发送了 -release 消息之后, result 就被清除了,而程序倚赖返回的result reference , 因此程序就可能崩溃了, 因为返回的reference 指向了一个早已被清除了的对象。
那末如何解决这个问题呢,既不会发生内存泄露或者返回一个无效的对象reference?答案就是使用 release pool (释放池) 。 一个临时存放对象的地方, 当一个对象被加入释放池中, 这个对象就注册了 - 稍候一定会接收到一个 -release 的消息。当另一个对象想要保留一个被调用对象的reference, 就会发送一个- retain 消息给那个要使用的对象。在未来某个时候,释放池会被清除,同时池内所有的对象被释放。池内任何对象的reference count 为零时, 这些对象就被清除。
来看两个情况:
情况1。
由于对象的retain count 是1,所以调用对象必须最终释放被条用的对象,否则会发生内存泄露。
情况2:
调用者并未retain 由方法返回的对象。结果, 释放池被清除时,对象也被清除了,所以调用者无须再释放对象了。
可以看到先前我们的问题得以解决, 对象的释放是发生在对象被方法返回之后,
Cocoa 的NSAutoreleasePool 类实现 释放池。 -autorelease 方法实现对象在释放池的添加和释放。
[object autorelease]; //将对象放入释放池,意味着稍后会被释放。
来看这个正确的方法:
-(NSObject *) correctMethod
{
NSObject *result = [[NSObject alloc] init];
[result autoreleqase];
return result;
}
allocated 初始化之后,reference 被赋值给 result. reference count =1。 当 result 被 autorelease 时,对象被放入释放池。 reference count 仍为1。
如果调用-correctMethod的代码没有retain 返回的对象 (NSObject) ,那末对象的 reference count 将为零, 对象被清除, 释放池被清除。 如果调用代码retain 住返回对象, 对象的reference count 将暂时为2。 当释放池最终被清除时,对象的reference count 减少到1。但对象并未被清除。 调用代码所keep 住的reference 依然有效,。
当然,特殊情况下,比如要加强程序性能和减低程序的内存要求,或者,比如制作一个命令行程序,不会用到 App Kit 程序框架时,就需要自己生成释放池。
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
在事件循环最后,释放这个池:
[pool release];
就减为0, B 就被清除(deallocated), 内存就被释放。清除B的时候, 被B所用的对象的 reference count 也可能减小, 也可能使它们被清除。
reference count (引用计数)是 NSObject 和 NSAutoreleasePool 两个类提供的机制。引用计数不是自动的, 编译器无法决定一个对象的生命周期,所以必须手动调用以下方法来控制 引用计数.
-
NSObject -retain reference count 增 1。 NSObject -release reference count 减一。 NSAutoreleasePool -autorelease 在将来的某时reference count 减一。 NSObject -alloc 为一个对象分配内存,并返回这个对象,reference count 为1。 NSObject -copy 生成一个对象的拷贝,并返回此对象, reference count 为 1。
[object retain];
同样
[object release];
减少reference count至零时,对象立即被清除, 此时,为了防止错误的发送消息给一个不存在的对象, 我们最好重新设置对象的引用, 通常使用 nil:
object = nil;
获取reference count 的值, 需要发送 retainCount 消息给对象。 这对调试程序有用。
总之,
以下每个方法必须有一个 -release 对应:
+alloc
+allocWithZone
-copy
-mutableCopy
-copyWithZone
-mutableCopyWithZone
-retain
同样
[object release];
减少reference count至零时,对象立即被清除, 此时,为了防止错误的发送消息给一个不存在的对象, 我们最好重新设置对象的引用, 通常使用 nil:
object = nil;
获取reference count 的值, 需要发送 retainCount 消息给对象。 这对调试程序有用。
- release 规则:
总之,
以下每个方法必须有一个 -release 对应:
+alloc
+allocWithZone
-copy
-mutableCopy
-copyWithZone
-mutableCopyWithZone
-retain
- 特殊情况
- (NSObject *) incorrectMethod1
{
NSObject *result = [[NSObject alloc] init];
return result;
}
错误是:
没有相对应 alloc 消息的 -release . 记住 :新的NSObject 实例生成时,reference count 为1, 并且实例被返回,但如果这个实例返回后不被使用, 那末就出现了内存泄露。
另一个错误的方法:
-(NSObject *) incorrectMethod2
{
NSObject *result = [[ NSObject alloc] init];
[result release];
return result;
}
错误:
虽然参照规则, 使用了- release 去对应 +alloc , 但是发送了 -release 消息之后, result 就被清除了,而程序倚赖返回的result reference , 因此程序就可能崩溃了, 因为返回的reference 指向了一个早已被清除了的对象。
那末如何解决这个问题呢,既不会发生内存泄露或者返回一个无效的对象reference?答案就是使用 release pool (释放池) 。 一个临时存放对象的地方, 当一个对象被加入释放池中, 这个对象就注册了 - 稍候一定会接收到一个 -release 的消息。当另一个对象想要保留一个被调用对象的reference, 就会发送一个- retain 消息给那个要使用的对象。在未来某个时候,释放池会被清除,同时池内所有的对象被释放。池内任何对象的reference count 为零时, 这些对象就被清除。
来看两个情况:
情况1。
step | 动作 | retain count |
1 | 对象被分配内存, (allocated) | 1 |
2 | 对象被加入释放池 | 1 |
3 | 对象由方法返回 | 1 |
4 | 对象被其他调用者 retain 住。 | 2 |
5 | 释放池被清除(deallocated), 对象被释放。 | 1 |
由于对象的retain count 是1,所以调用对象必须最终释放被条用的对象,否则会发生内存泄露。
情况2:
retain count | ||
1 | 对象被分配内存, | 1 |
2 | 对象被加入释放池 | 1 |
3 | 对象由方法返回 | 1 |
4 | 对象没有被调用者 retain 住。 | 1 |
5 | 释放池被清除, 对象被清除。 | 0 |
调用者并未retain 由方法返回的对象。结果, 释放池被清除时,对象也被清除了,所以调用者无须再释放对象了。
可以看到先前我们的问题得以解决, 对象的释放是发生在对象被方法返回之后,
- NSAutorleasePool 类
Cocoa 的NSAutoreleasePool 类实现 释放池。 -autorelease 方法实现对象在释放池的添加和释放。
[object autorelease]; //将对象放入释放池,意味着稍后会被释放。
来看这个正确的方法:
-(NSObject *) correctMethod
{
NSObject *result = [[NSObject alloc] init];
[result autoreleqase];
return result;
}
allocated 初始化之后,reference 被赋值给 result. reference count =1。 当 result 被 autorelease 时,对象被放入释放池。 reference count 仍为1。
如果调用-correctMethod的代码没有retain 返回的对象 (NSObject) ,那末对象的 reference count 将为零, 对象被清除, 释放池被清除。 如果调用代码retain 住返回对象, 对象的reference count 将暂时为2。 当释放池最终被清除时,对象的reference count 减少到1。但对象并未被清除。 调用代码所keep 住的reference 依然有效,。
- release pool 是谁生成的?
当然,特殊情况下,比如要加强程序性能和减低程序的内存要求,或者,比如制作一个命令行程序,不会用到 App Kit 程序框架时,就需要自己生成释放池。
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
在事件循环最后,释放这个池:
[pool release];
- release or autorelease ?
- 新规则:
- 当alloc, copy, retain 一个对象后, 日后你必须负责释放对象, 用- release 或者 -autorelease 。如果你没有 alloc, copy, 或者 retain 过一个对象, 那就没有必要去 release 它。
- alloc 或者 copy 之外的方法,当返回一个对象给你时,这个对象通常只在这个方法内有效,而且在方法的最后可以安全返回。你如果需要继续使用这个对象 (比如把对象存入一个实例变量), 就必须要磨 retain 要磨 copy 这个返回对象。
- 如果不再需要reference 一个对象,就应该使用 -autorelease 而不是 release. 同时,为了程序性能考虑, 尽量使用 -release 而不是 -autorelease.