注册 登录
主题 : Objective-C内存管理总结〜CC专版
级别: 精灵王

状态: 连续签到 - [8天]
UID: 1185
精华: 7
发帖: 145
可可豆: 25657 CB
威望: 25623 点
在线时间: 5079(时)
注册时间: 2008-11-01
最后登录: 2017-08-03
0 楼:  发表于: 2009-12-24 15:56    发自: Web Page

Objective-C内存管理总结〜CC专版   

管理提醒: 本帖被 gagaga 执行加亮操作(2009-12-24)


之前写过类似的文章,这篇以做总结,希望能帮助刚上船的兄弟。^_^

iPhone系统中的Objective-C的内存管理机制是比较灵活的,即可以拿来像C/C++一样用,也可以加个AutoreleasePool让它升级为半自动化的内存管理语言。当然,也不能拿JAVA虚拟机中的全自动化GC来比〜

一,引用计数是实例对象的内存回收唯一参考
引用计数(retainCount)是Objective-C管理对象引用的唯一依据。调用实例的release方法后,此属性减一,减到为零时对象的dealloc方法被自动调用,进行内存回收操作,也就是说我们永不该手动调用对象的dealloc方法。

它的内存管理API老简单老简单了,下面就是它主要操作接口:

1,alloc, allocWithZone,new(带初始化)
   为对象分配内存,retainCount为“1”,并返回此实例

2,release
   retainCount 减“1”,减到“0”时调用此对象的dealloc方法

3,retain
   retainCount 加“1”

4,copy,mutableCopy
   复制一个实例,retainCount数为“1”,返回此实例。所得到的对象是与其它上下文无关的,独立的对象(干净对象)。

5,autorelease
   在当前上下文的AutoreleasePool栈顶的autoreleasePool实例添加此对象,由于它的引入使Objective-C(非GC管理环境)由全手动内存管理上升到半自动化。


二,Objective-C内存管理准则
我们可以把上面的接口按对retainCount的操作性质归为两类,
A类是加一操作:1,3,4
B类是减一操作:2,5(延时释放)

内存管理准则如下:
1,A与B类的调用次数保持一制
2,为了很好的保障准则一,以实例对象为单位,谁A了就谁B,没有第二者参与

例:
复制代码
  1. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  2. NSObject *o = [[NSObject alloc] init];    //retainCount为1
  3. [o retain];    //retainCount为2
  4. [o release]; //retainCount为1
  5. [o autorelease]; //retainCount为1
  6. [pool release]; //retaincount为0,触发dealloc方法



三,对象的拥有者
面向对象领域里有个引用的概念,区别于继承,引用常被用来当做偶合性更小的设计。继承是强依赖,对吧。我们要降偶软件的设计,就要尽量减少对它的使用。但没有任何偶合的模块或功能是没有用的〜对吧,那我们只能多用引用了吧。一个实例拥有另一个实例的时候,我们称它为引用了另一个实例。

比如ClassA类的一个属性对象的Setter方法:

复制代码
  1. - (void)setMyArray:(NSMutableArray *)newArray {
  2.     if (myArray != newArray) {
  3.         [myArray release];
  4.         myArray = [newArray retain];
  5.     }
  6. }


假设这个类的一个实例为'a',调用setMyArray后,我们就可以说a拥有了一个新的myArray实例,也可以说a引用了一个新的myArray实例。其中调用的retain方法,使myArray的retainCount加一,我们需要注意以下两个地方:
1,setMyarray方法中,在retain之前先release了旧实例一次
2,在本实例的dealloc方法中,本应该是要再次release当前实例的,但回头看看参考内存管理准则。它并不合理,对吧。。。多了一次release。这里比较推荐的做法是:
[myArray setMyArray:nil];
这样可以巧妙的使当前实例release而不出错(我们可以向nil发送消息〜其实它本身就是个整数0),并符合我们的内存管理准则。更主要的是,很简单,你不需要考虑过多的事情。

另外一个比较容易忽略而又比较经典的问题是实例变量的循环引用,Objective-C为此区分了,其实也相当相当的简单:
1,强引用,上面讲的就是强引用,存在retainCount加一。
2,弱引用,但凡是assign声明并直接用指针赋值实现的被称之为弱引用,不存在retainCount加一的情况。

四,AutoreleasePool使Objective-C成为内存管理半自动化语言。
如果仅仅是上面这些,很简单,对吧。但往往很多人都会迷糊在自动内存管理这块上,感觉像是有魔法,但其实原理也很简单〜

先看看最经典的程序入口程序:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];

我们先把pool看成一个普通对象〜很简单,先是alloc,pool的retainCount为1。第三句release,retainCount为0,自动调用它的dealloc方法。它和任何其它普通对象没 任何区别。

魔法在哪里?
在声明pool后,release它之前的这段代码,所有段里的代码(先假设中间没有声明其它的AutoreleasePool实例),凡是调用了autorelase方法的实例,都会把它的retainCount加1,并在此pool实例中添1次此实例要回收的记录以做备案。当此pool实例dealloc时,首先会检查之前备案的所有实例,所有记录在案的实例都会依次调用它的release方法。

复制代码
  1. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  2. NSObject *o = [[NSObject alloc] init];
  3. [o autorelease];                                //在pool实例dealloc时,release一次此实例,重要的是并不是在此行去release
  4. NSLog(@"o retainCount:%d",[o retainCount]);    //此时还可以看到我们的o实例还是可用的,并且retainCount为1
  5. [pool release];    //pool 的 retainCount为0,自动调用其dealloc方法,我们之前备案的小o也将在这里release一次(因为咱们之前仅仅autorelease一次)


真对同一个实例,同一个Pool是可以多次注册备案(autorelease)的。在一些很少的情况化可能会出现这种需求:
复制代码
  1. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  2. NSObject *o = [[NSObject alloc] init];
  3. [o retain];
  4. [o autorelease];
  5. [o autorelease];
  6. [pool release];


我们调用了两次A类(retainCount加1的方法),使其retainCount为2,而接下来的两次autorelease方法调用,使其在pool中注册备案了两次。这里的pool将会在回收时调用此实例的两次release方法。使其retainCount降为0,完成回收内存的操作,其实这也是完全按照内存管理规则办事的好处〜

AutoreleasePool是被嵌套的!
池是被嵌套的,嵌套的结果是个栈,同一线程只有当前栈顶pool实例是可用的:

|  pool_3  |
|  ---------      |
|  pool_2      |
|  ---------   |
|  pool_1  |
|_______|

其代码如下:

复制代码
  1. NSAutoreleasePool *pool1 = [[NSAutoreleasePool alloc] init];
  2. NSAutoreleasePool *pool2 = [[NSAutoreleasePool alloc] init];
  3. NSAutoreleasePool *pool3 = [[NSAutoreleasePool alloc] init];
  4. NSObject *o = [[NSObject alloc] init] autorelease];
  5. [pool3 release];
  6. [pool2 release];
  7. [pool1 release];


我们可以看到其栈顶是pool3,o的autorelease是把当前的release放在栈顶的pool实例管理。。。也就是pool3。
在生命周期短,产生大量放在autoreleasePool中管理实例的情况下经常用此方法减少内存使用,达到内存及时回收的目的。

AutoreleasePool还被用在哪里?
在上面的例子里,也可以看到,我们在执行autorelease方法时,并没有时时的进行release操作〜它的release被延时到pool实例的dealloc方法里。这个小细节使我们的Objective-C用起来可以在方法栈中申请堆中的内存,创建实例,并把它放在当前pool中延迟到此方法的调用者释放〜


以上就是我想到的内存管理总结〜〜〜〜也就这么多吧〜日常工作用够用了〜不够的,没想到的大家补充〜


清空我的评分动态本帖最近评分记录: 共威望条评分记录
gagaga 贡献值 +5 2008-09-19
隐藏评分记录
旺财勇士
级别: 骑士

UID: 1439
精华: 1
发帖: 41
可可豆: 5194 CB
威望: 5154 点
在线时间: 1788(时)
注册时间: 2008-11-26
最后登录: 2017-06-01
1 楼:  发表于: 2009-12-24 16:15    发自: Web Page
Re:Objective-C内存管理总结~CC专版
谢过旺财兄,小生对内存管理的理解又深入一步    Orz.
如果你有空,可以找我聊天。
http://www.cocoachina.com/bbs/?u=1439
级别: 圣骑士

UID: 2228
精华: 4
发帖: 73
可可豆: 7020 CB
威望: 7020 点
在线时间: 501(时)
注册时间: 2009-02-16
最后登录: 2014-10-30
2 楼:  发表于: 2009-12-24 16:38    发自: Web Page
非常好的对以往同类文章的补充,支持了~~
级别: 圣骑士
UID: 7970
精华: 5
发帖: 30
可可豆: 5293 CB
威望: 5743 点
在线时间: 1770(时)
注册时间: 2009-08-20
最后登录: 2018-06-15
3 楼:  发表于: 2009-12-24 16:57    发自: Web Page
Re:Objective-C内存管理总结~CC专版
好文章,顶下
级别: 圣骑士

UID: 2406
精华: 1
发帖: 139
可可豆: 8145 CB
威望: 8145 点
在线时间: 1406(时)
注册时间: 2009-02-25
最后登录: 2015-08-25
4 楼:  发表于: 2009-12-24 17:34    发自: Web Page
Re:Objective-C内存管理总结~CC专版
确实写得很不错,已经比较完整了准确了,不得不顶。
不过现在retainCount是返回UINT_MAX不是引用数了。
important: This method is typically of no value in debugging memory management issues. Because any number of framework objects may have retained an object in order to hold references to it, while at the same time autorelease pools may be holding any number of deferred releases on an object, it is very unlikely that you can get useful information from this method.
The greatest test of courage on earth is to bear defeat without losing heart.
级别: 风云使者

UID: 7444
精华: 8
发帖: 326
可可豆: 61624 CB
威望: 63424 点
在线时间: 4572(时)
注册时间: 2009-07-31
最后登录: 2017-08-18
5 楼:  发表于: 2009-12-24 17:42    发自: Web Page
Re:Objective-C内存管理总结~CC专版
谢谢分享,认真学习下

开源项目WBShareKit,欢迎共同维护~绝世唐门
级别: 骑士
UID: 9828
精华: 0
发帖: 203
可可豆: 1904 CB
威望: 1904 点
在线时间: 500(时)
注册时间: 2009-10-16
最后登录: 2018-11-20
6 楼:  发表于: 2009-12-25 09:05    发自: Web Page
越看越迷糊啊。。哎。。。
symbian,wm,iphone开发中。。
级别: 新手上路
UID: 12179
精华: 0
发帖: 12
可可豆: 170 CB
威望: 120 点
在线时间: 395(时)
注册时间: 2009-12-24
最后登录: 2015-04-04
7 楼:  发表于: 2009-12-25 09:16    发自: Web Page
学习,感谢,帮顶
级别: 新手上路
UID: 313
精华: 0
发帖: 4
可可豆: 63 CB
威望: 63 点
在线时间: 156(时)
注册时间: 2008-07-24
最后登录: 2018-05-16
8 楼:  发表于: 2009-12-26 15:06    发自: Web Page
Re:Objective-C内存管理总结~CC专版
学习, 受教了, 谢谢LZ
iPhone , 爱疯, I 疯.
级别: 骑士
UID: 9966
精华: 0
发帖: 156
可可豆: 1497 CB
威望: 1497 点
在线时间: 157(时)
注册时间: 2009-10-22
最后登录: 2016-03-27
9 楼:  发表于: 2009-12-28 17:32    发自: Web Page
Re:Objective-C内存管理总结~CC专版
thanks mark

CocoaChina社区转载内容已尽可能注明出处,如未能核实来源或转发内容图片有权利瑕疵的,请及时联系社区进行修改或删除【联系方式QQ : 3442093904 邮箱:support@cocoachina.com】文章内容为作者独立观点,不代表CocoaChina社区立场。版权归原作者所有,如申请授权请联系作者,因文章侵权CocoaChina社区不承担任何法律及连带责任。

描述
快速回复

关注本帖(如果有新回复会站内信通知您)

发帖、回帖都会得到可观的积分奖励。查看论坛积分规则

按"Ctrl+Enter"直接提交
    顶部