什么是property?
property是一种代码生成机制,能够生成不同类型的getter/setter函数,特别是如果你想要用点(.)操作符号来存取变量的话,你就能必须使用property。
如何使用?
用法如:@property (attribute1,attribute2) float value;
这些attribute包括:
readonly-只读,只能读取而不能设定值(不能用setXXXX的函式)。
readwrite-可读可写(默认)。
assign-在设值时替换新旧变量(默认)。
retain-在设值时retain新的变量,release旧变量。
copy-在设值时copy一份新变量,release旧变量。
nonatomic-默认為atomic。
前两个只是简单的设定变量的可读写性。
assign只是简单的替换变量,通常运用在标量类型上,例如:NSInterger和CGRect,
或者(在引用计数环境)为那些你不拥有的对象,例如:delegates。
在垃圾收集环境下retain 和 assign实际上是相同的。
它的产生的setter代码內容类似:
- (void)setValue:(float)newValue {
value = newValue;
}
retain通常用在对象类型上
(
在mac os x v10.6之前的版本,它只能用在objective-c 中的对象类型上,因此你不能为core foundation 的对象类型上使用retain。
而mac os x v10.6和以后的版本上,你可以用__attribute__关键字来说明一个core foundation property在内存管理方面
应该和objective-c 对象同样对待:@property (retain)__attribute_((NSObject)) CFDictionaryRef myDictionary;
)
比方说要声明的property对象为NSString *string,
那么它的setter会是:
- (void)setString:(NSString*)newString {
if (string != newString) {
[string release];
string = [newString retain];
}
}
使用retain会通知编译器对新的变量发送保留(retain)的信息,以确保变量在程序支行时不会从记忆体中被清除。
copy也是通常用在必须是已经实现NSCopying协议的对象类型上,只是把刚刚retain的地方改成copy,在设定时不retain新的变量,而是copy一份給原变量成員。
通常在新变量為mutable而原变量為immutable时使用,使用copy可以确保原变量仍是immutable。
最后一個nonatomic,刚是与多线程有关。默认为atomic是在multi-thread的环境下才会用到。
在一般iPhone开发上,会设定为nonatomic来提高效率。
还有一点非常要注意的就是,在使用property时一定要在前面带上self(如:self.xxx),如果你不这样做,容易造成内存
泄漏。
下面是一个不好的例子:
- @interface testViewController : UIViewController { NSString *teststring_A; NSString *teststring_B; IBOutlet UITextField *textfield_1; IBOutlet UITextField *textfield_2;}@property (nonatomic, retain) NSString *teststring_A;@property (nonatomic, retain) NSString *teststring_B;- (IBAction)action1:(id)sender;- (IBAction)action2:(id)sender;@end@implementation testViewController@synthesize teststring_A;@synthesize teststring_B;- (void)dealloc { [super dealloc];}- (IBAction)action1:sender{ teststring_A = textfield_1.text ; NSLog(@"teststring_A in action 1 is : %@/n", teststring_A); teststring_B = textfield_2.text ; NSLog(@"teststring_B in action 1 is : %@/n", teststring_B);}- (IBAction)action2:(id)sender{ NSLog(@"teststring_A in action 2 is : %@/n", teststring_A); NSLog(@"teststring_B in action 2 is : %@/n", teststring_B);}
在这个例子中点第一个按钮后,在点第二个按钮时,程序
输出为:
- 2010-11-19 15:32:14.827 test[419:207] teststring_A in action 1 is : 1232010-11-19 15:32:14.829 test[419:207] teststring_B in action 1 is : 4562010-11-19 15:32:14.927 test[419:207] teststring_A in action 2 is : 1232010-11-19 15:32:14.929 test[419:207] teststring_B in action 2 is : {( >)}
可以看出的teststring_B的值已经不对了,而且有时候程序可能会崩溃掉!
为什么不加self会有这么严重的后果呢?
网上查到一篇讲得很好的解释。
大至意思是这样:
变量和属性是不同的,属性是建立在变量的基础上的,但是实际上它是方法。
在这里你定义了一个名为teststring_B的属性,这个属性retain了赋给它的任何东西(当然也释放了旧的值)。
与这个属性等同的方法看赶来像这样(简化版):
- - (NSString *)teststring_B { // Return the content of the variable teststring_B. return teststring_B;}- (void)setTeststring_B:(NSString *)val { // Retain new value. [val retain]; // Release old value in variable teststring_B [teststring_B release]; // Assign the new, retained value to variable teststring_B teststring_B = val;}
同在你可以以两种方式使用这个属性:
[self setTeststring_B:foo] 或者slef.teststring_B = foo;
重点是后面一种使用方式仅仅是一种方便的写法,编译器将会把它翻译成前面一种的形式,
即:把slef.teststring_B = foo; 转化成[self setTeststring_B:foo]。
现在来解释一下为什么程序崩溃掉:
你已经获得了一个很有可能会被autoreleased的string value,因为前面没有加self,
现在你只是很简单的把这个值赋给变量teststring_B,而不是属性teststring_B。
而且,你也忘记了应该要retain这个在属性中会为你retain的值。
现在变量teststring_B被赋给的值是autorelease的(它不知道你已经有一个变量一直指向这个值)
然后,最好的情况是一个新的对象被生成,而且内存正好分配在将被autorelease的值的内存,这样即使这个值被release了,
你在后面使用的时候程序也不会崩溃。但是不管在什么情况下,变量teststring_B指向的内存中的值已经不是你认为它应该是
的值了,可能是别的其它变量的值(或者垃圾)。
你可以这样来修改你的程序:
- // First retain, then release; it might be the same object// and if you would release it first its retain count might// drop to 0 and get cleaned up before you can retain it again.NSString *tmp = [textfield_2.text retain];[teststring_B release];teststring_B = tmp;// Better !self.teststring_B = textfield_2.text;
property实现的指令有两个:@synthesize和@dynamic
@synthesize没啥可说的,说说@dynamic吧。
您可以使用@dynamic关键字来告诉编译器,你将通过属性暗示或者直接用方法实现或在运行时使用诸如代码动态加载或其动态方法解析机制来履行此API的约定。
它会抑制编译器在不能找到合适的实现时生成的警告。当您知道该方法将在运行时可用时,就可以使用它。
下面是一个使用的例子:
- @interface MyClass : NSManagedObject { } @property(nonatomic, retain) NSString *value; @end@implementation MyClass @dynamic value; @end
有关于Property Redeclaration
你可以在子类中重新声明一个属性,但是你必须在子类中完全重复在父类中已有的其它attributes(除了readonly 与readwrite)。
这同样适用于为一个类别或协议重新声明属性的情形,虽然属性可以在一个类别或协议中被重新声明,但该属性的attributes
必须是要被完全重复。
如果你声明一个类的属性为只读,你可以在类扩展、协议或子类中重新声明它为readwrite的。
在重复声明一个类的扩展,该属性被重新声明在任何@synthesize产生setter之前。能够重新声明一个只读属性为读/写的能力,
使得两个常用的实现模式成为可能:一是不可变类的可变子类(NSString,NSArray和NSDictionary都是例子),二是属性有一个公共API,它是只读的,但在类的内部有一个私有的readwrite实现。
下面的例子显示了使用类扩展,提供一个声明为只读的公共头,但私下重新声明为读/写属性。
- // public header file @interface MyObject : NSObject { NSString *language;} @property (readonly, copy) NSString *language; @end// private implementation file @interface MyObject () @property (readwrite, copy) NSString *language; @end@implementation MyObject @synthesize language; @end
来说说copy。
当我们需要一个新的副本时,可以使用copy修饰。拿一个nsstring对象来举例,假设有一个person类,它的name属性如下:
- <pre name="code" class="cpp">@property(nonatomic,copy) NSString *name;
这样当遇到如下情况:
- NSMutableString *someName = [NSMutableString stringWithString:@"Chris"];
- Person *p = [[[Person alloc] init] autorelease];
- p.name = someName;
- [someName setString:@"Debajit"];
someName对象在赋值给name后值又改变了,但p.name的值并不受影响。
如果person类的name属性写成这样:
- @property(nonatomic,retain) NSString *name;
那么someName的改变则会影响到name的值。