注意到经常有一个这样的问题:某指针对象先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]
UIView *view1=[[UIView alloc] init]; UIView *view2=[view1 retain]; int i=[view1 retainCount]; NSLog(@"i:%d",i); [view1 release]; view1=nil; [view1 addSubview:view2]; 整个程序运行,是不会crash的。但是存在内存泄露。但是,现在又有另外一个问题,请看下列代码:
[cpp]
UIView *view1=[[UIView alloc] init]; UIView *view2=[view1 retain]; int i=[view1 retainCount]; NSLog(@"i:%d",i); [view1 release]; view1=nil; [view1 addSubview:view2]; i=[view1 retainCount]; NSLog(@"i:%d",i); i=[view2 retainCount]; NSLog(@"i:%d",i); [view2 release]; i=[view2 retainCount]; 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]
2011-12-09 01:17:07.364 ReleaseNildemo[19115:f803] i:2 2011-12-09 01:17:07.365 ReleaseNildemo[19115:f803] i:0 2011-12-09 01:17:07.366 ReleaseNildemo[19115:f803] i:1 2011-12-09 01:17:07.367 ReleaseNildemo[19115:f803] i:1 为什么会这样?按推理说,程序执行到输出这句应该crash才对,我运行了好几遍,确实没有出现。晚上在网上和其他程序员讨论这个问题,有人运行的情况是“有时候会crash,有时候不会。断点的话不会crash”,最后大家讨论的话题开始变成“系统是先输出还是先回收?”,更有甚者,有人输出了一下内存地址,发现是view2 release前后输出的是同一个内存地址,说明,这个东西内存虽然释放了,但是系统还没来得及回收。有时候crash,原因是你向一块未申请的内存发送了一条消息,最终成了系统回收速度问题了,所以,个人认为,以后碰到类似这种问题,干脆手动init,release,至少这样能很快释放,更便于清楚当前对象的内存情况。