关于@property关键字,网上搜出来一箩筐,但是总让人感觉杂乱无章,没法真正系统地理过一遍,这里我以自己的理解对这个东东进行系统的倒腾倒腾,哪里有问题希望各位指出.
首先,请大家先遗忘@property这个关键字,看看下面这个例子能否赋值并输出"_name"这个成员变量的值
//MyClass.h
@interface MyClass:NSObject{
NSString * _name;
}
@end
//MyClass.m
@implementation MyClass
@end
//main.mm
MyClass *myClass = [[MyClass alloc] init];
myClass.name = @"Jack";
NSLog(@"myClass Name is:%@",myClass.name);
很明显Xcode肯定直接显示"myClass.name = @"Jack";"这句错误.
第二步接着我们对MyClass.h和MyClass.m稍做修改,改成如下
//MyClass.h
@interface MyClass:NSObject{
NSString * _name;
}
- (NSString*) name;
- (void) setName:(NSString*)newName;
@end
//MyClass.m
@implementation MyClass
- (NSString*) name{
return _name;
}
- (void) setName:(NSString*)newName{
if (_name != name) {
[_name release];
_name = [newName copy];
}
}@end
//main.mm
MyClass *myClass = [[MyClass alloc] init];
myClass.name = @"Jack";
NSLog(@"myClass Name is:%@",myClass.name);
这时我们发现执行成功了,输出了Jack,如果查看内存,其实还可以发现myClass这个对象的_name这个成员变量值也变成了@"Jack",为什么会这样?我的理解是这样,在xcode的编译器中,对于"."的这种写法,编译器会去自动搜索类中有没有对应定义的getter和setter函数,此例中"myClass.name"这种写法,编译器就会去搜索有没有形如"- (id) name"和"- (void) setName:(id)newName;"的方法,有的话则相应调用该方法,没的话则报错.所以才会出现我们以上举的这两个例子的情况.
第三步我们继续对MyClass.h和MyClass.m稍做修改,去掉我们前面第二步加的两个方法,加上@property和@@synthesize关键字,修改后如下:
//MyClass.h
@interface MyClass:NSObject{
NSString * _name;
}
@property(nonatomic,copy) NSString *name;
@end
//MyClass.m
@implementation MyClass
@synthesize name = _name;
@end
//main.mm
MyClass *myClass = [[MyClass alloc] init];
myClass.name = @"Jack";
NSLog(@"myClass Name is:%@",myClass.name);
我们发现此时照样执行成功,这个就是因为@property和@synthesize的贡献啦,在xcode编译器中,@property和@synthesize其实就是一种关键字,当编译器看到这个关键字的时候,编译器会自动为我们声明和实现我们前面第二步手动增加的那两个方法(当然这里也可能只有一个getter方法,或者在setter方法中实现可能和我们前面写的不一样,这个待后续讲property属性时再作具体说明),所以达到了我们第二步中一样的效果.
这里有两点需要额外说明下:第一,假设我们在MyClass.h文件中没有声明"_name"这个成员变量,那么编译器当看到@synthesize name = _name时会自动帮我们声明"_name"这个成员变量;第二,在xcode4.4之后,我们可以省略@synthesize关键字,只要我们写了@property(nonatomic,copy) NSString *propertyName,编译器会自动帮我们加上类似"@synthesize propertyName = _propertyName"的语句.
如果大家能理解下面两个例子,那么我前面说的大家应该也差不多理解了.
例子一
//MyClass.h
@interface MyClass:NSObject{
NSString * _nnnnnnnnname;
}
@property(nonatomic,copy) NSString *name;
@end
//MyClass.m
@implementation MyClass
@synthesize name = _nnnnnnnnname;
@end
//main.mm
MyClass *myClass = [[MyClass alloc] init];
myClass.name = @"Jack";//此时成员变量_nnnnnnnnname变成了@"Jack"
NSLog(@"myClass Name is:%@",myClass.name);//此时输出其实是_nnnnnnnnname这个成员变量的值
例子二
//MyClass.h
@interface MyClass:NSObject{
NSString * _name;
}
@property(nonatomic,copy) NSString *name;
- (NSString*) name;
- (void) setName:(NSString*)newName;
@end
//MyClass.m
@implementation MyClass
@synthesize name = _name;
- (NSString*) name{
NSLog(@"My redefined getter func");
return _name;
}
- (void) setName:(NSString*)newName{
NSLog(@"My redefined setter func");
if (_name != name) {
[_name release];
_name = [newName copy];
}
}
@end
//main.mm
MyClass *myClass = [[MyClass alloc] init];
myClass.name = @"Jack";//此时会输出"My redefined setter func",因为由@property关键字导致的编译器自动生成的setter函数被覆盖了
NSLog(@"myClass Name is:%@",myClass.name);//此时会输出"My redefined getter func",因为由@property关键字导致的编译器自动生成的getter函数被覆盖了
下面我们来谈谈@property的修饰属性.
根据我目前的理解,@property的修饰属性有三类:只读性(readwrite/readonly);原子性(nonatomic/atomic);写内存方法(assign/retain/copy/weak/strong).
只读性:顾名思义,表示该属性是否只读,默认是readwrite,即编译器会生成getter和setter方法,若设置为readonly,编译器只会生成getter方法.
原子性:和Java中原子性一个概念,表示是否支持类似多线程时锁定等待.默认atomic即启动线程保护,有线程对该值读写未完成时锁定不被其他线程访问.
写内存方法性:
//assign方式,在设值时替换新旧变量(默认),适用简单数据类型,如:NSIngeter/CGFloat/int/char等
//这里不确定对象能否支持assign
- (void) setName:(NSString*)newName{
if (_name != newName) {
_name = newName;
}
}
//retain方式,在设值时retain新的变量,release旧变量,适用类对象,除了NSString这种特殊的类对象
- (void) setName:(NSString*)newName{
NSLog(@"My redefined setter func");
if (_name != name) {
[_name release];
_name = [newName retain];
}
}
//copy方式,在设值时copy一份新变量,release旧变量,一般用于NSString
- (void) setName:(NSString*)newName{
NSLog(@"My redefined setter func");
if (_name != name) {
[_name release];
_name = [newName copy];
}
}
//网上找来一句话,参考参考吧
//assign/retain/weak/strong,在非ARC环境下,assign为默认,引用计数不变;retain引用计数加1;
//在引用计数环境下,默认为strong,与retain作用相同;从5.0系统后引入weak,作用与assign相似,不过当所指向对象引用为0时,自动置为nil