关闭

内存管理 reference count

321人阅读 评论(0) 收藏 举报
 

HTML Tags and JavaScript tutorial



内存管理 reference count




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

-retain

reference count 增 1。



 NSObject

-release

reference count 减一。



 NSAutoreleasePool

-autorelease

在将来的某时reference count 减一。



 NSObject

-alloc

为一个对象分配内存,并返回这个对象,reference count 为1。



 NSObject

-copy

生成一个对象的拷贝,并返回此对象, reference count 为 1。






发送+alloc 或者 +allocWithZone: 消息给类, 就会预留出内存给一个新的实例, reference count 为 1。 通过 retain ,  其他对象可以引用这个类实例。




[object retain];
同样
[object release];
减少reference count至零时,对象立即被清除, 此时,为了防止错误的发送消息给一个不存在的对象, 我们最好重新设置对象的引用, 通常使用 nil:
object = nil;
获取reference count 的值, 需要发送 retainCount 消息给对象。 这对调试程序有用。


release 规则:


总之,
以下每个方法必须有一个 -release 对应:
+alloc
+allocWithZone
-copy
-mutableCopy
-copyWithZone
-mutableCopyWithZone
-retain



特殊情况

有时, reference count 不这磨简单。 当需要返回一个对象的引用时, 下面这个方法是错误的:
- (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 是谁生成的?

Cocoa 程序使用App Kit 框架,在内部事件循环(event loop) 开始的时候自动生成release pool.  当程序代码执行结束, 控制返回到应用程序对象时(通常在事件循环的最后),应用程序对象发送 release 消息给 释放池。
当然,特殊情况下,比如要加强程序性能和减低程序的内存要求,或者,比如制作一个命令行程序,不会用到 App Kit 程序框架时,就需要自己生成释放池。
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
在事件循环最后,释放这个池:
[pool release];


release or autorelease ?

-release 方法要远比 -autorelease 效率高。 所以, 除非要使用 -autorelease 的特殊功能, 否则,尽量使用 - release. 过多使用 -autorelease 将导致程序运行缓慢。


新规则:



当alloc, copy, retain 一个对象后, 日后你必须负责释放对象, 用- release 或者 -autorelease 。如果你没有 alloc, copy, 或者 retain 过一个对象, 那就没有必要去 release 它。

alloc 或者 copy 之外的方法,当返回一个对象给你时,这个对象通常只在这个方法内有效,而且在方法的最后可以安全返回。你如果需要继续使用这个对象 (比如把对象存入一个实例变量), 就必须要磨 retain 要磨 copy 这个返回对象。

如果不再需要reference 一个对象,就应该使用 -autorelease 而不是 release. 同时,为了程序性能考虑, 尽量使用 -release 而不是 -autorelease.



0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:150462次
    • 积分:4028
    • 等级:
    • 排名:第7666名
    • 原创:242篇
    • 转载:0篇
    • 译文:0篇
    • 评论:16条
    文章存档
    最新评论