@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方法能用了吧?

后续:哪错了告知下,感激不尽!!

posted @ 2012-12-08 14:06  Maxfong  阅读(2785)  评论(0编辑  收藏  举报