(iPhone/iPad)浅谈Objective-C中release和nil的关系

注意到经常有一个这样的问题:某指针对象先release后=nil,这里后跟个=nil有什么作用?不写行不行?

简单一点说是,release是用来释放内存,nil是将对象指针设为null,nil本身对内存没什么影响,但他处理指针,尤其是避免野指针倒是很有必要。

举一个例子:

NSString *str=[[NSString alloc] init];

当我不需要str时

执行[str release];

str 的retain值减1,但是如果当前retain值>0,却紧随其后加一句str=nil;那么这时[str retainCount]应该为0,因为[nil retainCount]==0;但这时很明显存在内存泄露。

所以一个很好的写作习惯是:

当对象的retainCount==1时,写完[str release];后接着写上str=nil;       这样,当在后面的代码再次调用str相关的方法属性,也不会报错,因为之前已经将str设置为空指针,再调用str的方法也会被认为是null,不会真正调用,更不会报错。

例如下列代码:(以下所有代码段都已经过实际操作验证)

[cpp]
  1. UIView *view1=[[UIView alloc] init];  
  2.     UIView *view2=[view1 retain];  
  3.     int i=[view1 retainCount];  
  4.     NSLog(@"i:%d",i);  
  5.     [view1 release];  
  6.     view1=nil;  
  7.     [view1 addSubview:view2];  
整个程序运行,是不会crash的。但是存在内存泄露。

但是,现在又有另外一个问题,请看下列代码:

[cpp]
  1. UIView *view1=[[UIView alloc] init];  
  2.     UIView *view2=[view1 retain];  
  3.     int i=[view1 retainCount];  
  4.     NSLog(@"i:%d",i);  
  5.     [view1 release];  
  6.     view1=nil;  
  7.     [view1 addSubview:view2];  
  8.     i=[view1 retainCount];  
  9.     NSLog(@"i:%d",i);  
  10.     i=[view2 retainCount];  
  11.     NSLog(@"i:%d",i);  
  12.     [view2 release];  
  13.     i=[view2 retainCount];  
  14.     NSLog(@"i:%d",i);  
请问终端输出的log应该是什么?

第一个i=2,没问题,因为view1 init了一次,retain了1次,retain值为2。

第二个i=0,根据上面所讲的推断,也没问题,因为之前view1=nil,空指针的retainCount值为0。

第三个i=1,也没什么问题,因为view2是一个被赋了值的新指针,它不同于指针view1,二者是2个独立的指针,而且,view2还被赋了值分配了内存地址。

但是,第四个i呢?i=?

答案是i=1。为什么会这样?下面是真实输出结果:

[cpp]
  1. 2011-12-09 01:17:07.364 ReleaseNildemo[19115:f803] i:2  
  2. 2011-12-09 01:17:07.365 ReleaseNildemo[19115:f803] i:0  
  3. 2011-12-09 01:17:07.366 ReleaseNildemo[19115:f803] i:1  
  4. 2011-12-09 01:17:07.367 ReleaseNildemo[19115:f803] i:1  
为什么会这样?按推理说,程序执行到输出这句应该crash才对,我运行了好几遍,确实没有出现。晚上在网上和其他程序员讨论这个问题,有人运行的情况是“有时候会crash,有时候不会。断点的话不会crash”,最后大家讨论的话题开始变成“系统是先输出还是先回收?”,更有甚者,有人输出了一下内存地址,发现是view2 release前后输出的是同一个内存地址,说明,这个东西内存虽然释放了,但是系统还没来得及回收。

有时候crash,原因是你向一块未申请的内存发送了一条消息,最终成了系统回收速度问题了,所以,个人认为,以后碰到类似这种问题,干脆手动init,release,至少这样能很快释放,更便于清楚当前对象的内存情况。

以上是我对release nil操作的一些理解,欢迎更多人加入讨论中。

相关推荐