原文地址:http://www.iphonedevsdk.com/forum/iphone-sdk-tutorials/7295-getters-setters-properties-newbie.html
假设有个MyClass类,里面定义了一个成员text。对它的读与写如下所示:
//MyClass.h file @interface MyClass: NSObject { NSString *text; } -(void) init; -(void) logText; @end //MyClass.m file @implementation MyClass - (void)init { text = @"some text"; } - (void)logText { NSLog(@"%@", text); } @end
不过,我们肯定是希望在运行时能够改变它,那么需要添加新函数:
//MyClass.h file @interface MyClass: NSObject { ... -(NSString*) text; // 这就是getter的声明 -(void) setText:(NSString *)textValue; // 这就是setter的声明 @end //MyClass.m file @implementation MyClass ... // 这就是getter的定义 -(NSString*) text { return text; } // 这就是setter的定义 -(void) setText:(NSString *)textValue { if (textValue != text) // 若两个对象不同 { [textValue retain]; // “传入对象”的引用计数加一 [text release]; // text的引用计数减一(释放堆内存) text = textValue; // 给text赋新值 } } -(void)dealloc { [text release]; // 对象的析构函数里要释放text [super dealloc]; } @end
NSString *theTextValue = [obj text]; // 读 [obj setText:newStringValue]; // 写
-(void) setText:(NSString *)textValue { [textValue retain]; [text release]; text = textValue; }
-(void) setText:(NSString *)textValue { [text release]; // 先减一 [textValue retain]; // 再加一 text = textValue; }
//MyClass.h file @interface MyClass: NSObject平共处{ NSString *text; int value; } -(void) init; -(void) logText; -(NSString*) text; -(void) setText:(NSString *)textValue; -(int) value; -(void) setValue:(int*)intValue; @end //MyClass.m file @implementation MyClass - (void)init { text = @"some text"; value = 2; } - (void)logText { NSLog(@"%@", text); } -(NSString *) text { return text; } -(void) setText:(NSString *)textValue { if (textValue != text) { [textValue retain]; [text release]; text = textValue; } } -(int) value { return value; } -(void) setValue:(int)intValue { value = intValue; } -(void)dealloc { [text release]; [super dealloc]; } @end
//MyClass.h file @interface MyClass: NSObject { NSString *text; int value; } @property(nonatomic, retain) NSString *text; @property(nonatomic, assign) int value; -(void) init; -(void) logText; @end //MyClass.m file @implementation MyClass @synthesize text; @synthesize value; - (void)init { text = @"some text"; value = 2; } - (void)logText { NSLog(@"%@", text); } -(void)dealloc { [text release]; [super dealloc]; } @end
NSString *s = [obj text]; [obj setText:@"new string"]; int i = [obj value]; [obj setValue:3];用点引用法,就可以写成:
NSString *s = obj.text; obj.text = @"new string"; int i = obj.value; obj.value = 3;
注意,二者在功能上是完全一样的,都是对getter和setter的调用!用点引用法只是看起来简洁一点而已。
void doSomething(String text) { this.text = text; }
-(void)doSomething:(NSString *)text { self.text = text; }
再看下面的函数,一样具有迷惑性:
-(void)doSomething:(NSString *)input { text = input; // 直接对成员赋值。对NSString对象来说,会有内存泄漏 self.text = input; // 会调用setter。保证了成员先释放再赋值,安全 }所以,使用“self.text =”这种赋值形式是好习惯。
-(void)doSomething { SomeObject *obj = [[SomeObject alloc] init]; self.obj = obj; [obj release]; }第一行:分配、初始化后,obj就有了“一个”引用。
对象的直接赋值只能出现在一个地方:init。因为它只在alloc之后调用一次:
obj = [[SomeObject alloc] init];而且,此时所有成员都是nil,直接赋值无妨。
self.obj = [[SomeObject alloc] init];
像这样使用了setter的,obj会有两个引用!一个对象刚刚初始化就有了两个引用!它很可能永远不会被释放了。内存泄漏啊。
如果想使用“点引用法”,同时又对编译器产生的setter不满意,怎么办?
以上面的程序为例,如果我们想把text和value联系起来(在修改value的时候,text也跟着变),那么:
-(void) setValue:(int)intValue { value = intValue; self.text = [NSString stringWithFormat:@"%d", intValue]; }同时,去掉@synthesize value,换成@dynamic value。这意味着你要提供自己的setter给编译器。
有时,你要创建一个对象给别人使用,那么你就无法选择时机去释放它了。怎么办?
- (MyObject *)getObject { return [[MyObject alloc] init]; }这样是不行的。使用者可能会在使用完后忘记释放它。根据程序界的规律,如果程序员可能忘,那他一定会忘。
- (MyObject *)getObject { return [[[MyObject alloc] init] autorelease]; }调用了autorelease方法,系统会在NSAutoreleasePool里注册该对象。这样,“减引用计数”这个责任就落到了“自动释放池”的身上。池负责在合适的时机调用该对象的release方法。
再看上面提到的两条原则:
1. 对象的创建者有责任去释放。反之,不是你创建的,你也不要去释放它。使用者无责任。池会帮你释放。
2. 使用了retain的,仍然有责任去release。因为retain之后,增加了引用。池最后释放的时候,会因为该对象引用数不为零而不释放它。释放的责任在于使用者。
比如前面用到的代码:
-(void) setValue:(int)intValue { value = intValue; self.text = [NSString stringWithFormat:@"%d", intValue]; }临时对象NSString的释放就是池负责的。如果该函数的上下文没有池,内存就泄漏了。
@property (nonatomic, readonly) PropertyType propertyName;
若想让对象可读可写,可以用:
@property (nonatomic, readwrite) ...
但这是编译器的默认行为,不写也可以。
编译器还默认对象为“atomic”,即多线程环境下保证安全。不会在多线程同时写,或者一个写一个读的时候发生数据破坏。
除非在你的程序中使用了多线程,否则所有的调用都在一个线程里进行。这样,加上nonatomic可以提高程序的运行效率。
给对象起“别名”也很有用。
@interface MyClass: NSObject { NSString *_text; } @property(nonatomic, retain) NSString *text; ... @synthesize text = _text;这样,类的属性就更名为“text”了。