@property retain 引发的思考
@property 官方解释:Declared Properties
个人理解:@property的意思是自动帮添加get、set方法。
而NSString等引用类型,需要使用retain。
当我们不手动添加@property,如,建立一个私有变量,自己写get、set方法,通过消息发送,获取变量值。
这里,如果此变量类型不为值类型,是否需要在set方法中,做@property的retain所做的事。
个人觉得是需要的,即
//H @property (nonatomic, retain) NSString *value; //M -(void)setValue:(NSString *)_value{ if (value != _value) { [value release]; value = [_value retain]; } }
但是有人觉得需要增加一步操作:
//M -(void)setValue:(NSString *)_value{ if (value) { if (value != _value) { [value release]; value = [_value retain]; } } }
当原来的value值不存在,那执行[value release]不就crash了?
理论上貌似对的,但OC中是消息机制,即发送一个消息给对象,不响应,不会做任何操作。有人说:
NSArray *array = [[NSArray alloc] init]; [array addObject:@"0"];
这样就crash了。是的,这样肯定crash,因为他响应了。
如何才不响应?当对象是nil的时候(0x00)。
即为什么我们release一个对象后,还要让他等于nil。
当release一个对象(A)后,该对象(A)不会立即Free,而是retainCount减1且标记内存(0x01)为可用。
当该内存(0x01)被另一个对象(B)alloc后,该对象(A)的指针所指向的地址实际上是错误的,对A发送消息,响应了,所以crash了。(就是野指针)
既然这样,原本if (value)完全不需要,第一次执行setValue时候,[value release]实际上执行的是:[nil release],毫无crash迹象。
朋友说:
-(void)setValue:(NSString *)_value{ value = _value; }
这样不就行了?是的,这是@property assign 写法,值类型,直接栈中分配,我们不需要Free。
但,对于引用类型,这样操作应该是将value指针强行指向_value,新值赋值成功,但原指针指向的堆里的内存怎么办?
这块只有当app close的时候,才会Free。当你的app内存占用越来越多而引发的一系列后果,你懂的!!!
那为什么加retain?
value = [_value retain];
传进来的_value值可能是autorelease,或者是xxxx,保存是应该的。
这样,那set方法应该是没问题了,我们测试下retainCount。
-(void)setValue:(NSString *)_value{ if (value != _value) { NSLog(@"%d", [value retainCount]); [value release]; NSLog(@"%d", [value retainCount]); value = [_value retain]; NSLog(@"%d", [value retainCount]); } }
并执行:
[self setValue:@"1"]; [self setValue:@"2"];
得到的结果是:
2012-12-08 13:21:57.255 releaseTest[665:c07] 0 2012-12-08 13:21:58.961 releaseTest[665:c07] 0 2012-12-08 13:22:00.862 releaseTest[665:c07] -1 2012-12-08 13:22:06.887 releaseTest[665:c07] -1 2012-12-08 13:22:08.478 releaseTest[665:c07] -1 2012-12-08 13:22:09.349 releaseTest[665:c07] -1
执行6次,除了前2次为空,reatinCount为0外,其他4次都是有值的,居然为-1.
为什么?
其实:NSString为特殊,除了以[[NSString alloc] initWithFormat:@"0"]等几个特殊的方法创建的string,其他都是字符串常量,retainCount为x_MAX,是不能release的,所以大段的字符串常量内容,最好放在资源文件中进行读取。
当你创建自己写的Class时,retainCount感觉就正常了。
现在,set方法能用了吧?
后续:哪错了告知下,感激不尽!!