34 属性
34.1属性的特性
存取类型
任何一个属性可以声明为readwrite或readonly。默认为readwrite。
生命周期类型
(lifetime specifier)的特性包括,unsafe_unretained,assign,strong,weak,copy。这些特性决定了存方法将如何处理与其相关的内存管理问题
assign是默认的也是最简单的:存方法会将传入的值直接赋给实例变量。以下面这段声明及定义为例:
@property (assign) int averageScore;
//等同与@property int averageScore;
这段代码等同于实现了以下存方法:
- (void)setAverageScore: (int)d {
averageScore = d;
}
copy
copy特性要求拷贝传入的对象,并将新对象赋给实例变量。以下面这段声明及定义lastName的代码为例
@property (copy) NSString *lastName;
//以上方法等同于你下代码
- (void)setLastName :(NSString *)d{
lastName = [d copy];
}
有些类会有特定的子类,这些子类是可修改的,这种类型的对象最适合使用copy特性。比如NSString,就有名为NSMutableString的子类。而向setLastName:方法传入NSMutableString对象是有效的。
NSMutableString *x = [[NSMutableString alloc] initWithString:@"Ono"];
//将新创建的对象传入setLastName
[myObj setLastName:x];
//因为setLastName方法会拷贝传入对象,所以修改x不会对实例变量产生影响。
[x appendString:@"Lennon"];
如果传入的对象不是可修改的:NSObject的copy方法其实仅仅是调用copyWithZone,并将nil作为实参传入,不可修改的类通常会被覆盖copyWithZone方法,以优化拷贝过程,以NSString为例,她的copyWithZone方法的示例代码如下:
- (id)copyWithZone:(NSZone *)z {
return self;
}
也就是说,NSString对象不会真的拷贝出一个新对象。
有些类会有两个版本:一个是可修改的,一个是不可修改的,无论是哪个版本,copy方法都会返回不可修改的版本,例如NSMutableString的copy方法会返回NSString实例,如果要拷贝出可修改对象,就要使用mutableCopy
Objective-C没有为属性提供mutableCopy这样的特性,如果某个存方法需要赋值传入的对象,并且要求新对象是可修改的,就必须自己编写代码实现(向传入的对象发送mutableCopy消息),而不能依赖属性机制,例如,可以为BNEOwnedAppliance编写出如下的自定义方法setOwnerNames:
- (void)setOwnerNamesInternal:(NSSet *)newNames {
_ownerNamesInternal = [newNames mutableCopy];
}
再谈对象拷贝
大多数不是来自苹果公司的Objective-C类并没有实现copyWithZone方法
NSObject类的copy方法和mutableCopy方法的实现代码大致如下:
- (id)copy {
return [self copyWithZone:NULL];
}
-(id)mutableCopy {
return [self mutableCopyWithZone:NULL];
}
因此,如果编译以下这段代码
BNRAppliance *b = [[Appliance alloc] init];
BNRAppliance *c = [b copy];
//编译器会报如下错误
-[BNRAppliance copyWithZone:]: unrecognized seletor sent to instance xxxxxxx
copyWithZone:方法以及mutableCopyWithZone:方法分别在NSCoping与NSMutableCoping协议中进行了声明。这两个协议,大部分FOundation框架的类都至少符合一个。
atomic与nonatomic
属性默认是atomic。所以必须显式添加nonatomic。
34.2实现存取方法
编译器会默认认为你声明的所有属性合成存取方法。通常来说,实现存取方法很简单,因此很适合交给编译器来做
有时候,你需要使用存取方法来处理一些特殊逻辑,这种情况下,就需要自己实现存取方法
1.修改实例变量时,需要更新app的用户界面
2.修改实例变量时,需要更新一些缓存信息
例如,你在头文件中声明了一个属性
@property (nonatomic, copy) NSString *currentState;
当对象调用setCurrentState:方法的时候,你希望这个方法还能实现其他的
- (void) setCurrentState:(NSString *)currentState {
_currentState = [currentState copy];
//更新用户界面
······
}
如果你声明一个属性,手动实现存取方法,编译器就不会合成实例变量。
但如果你需要实例变量,就必须自己创建,创建的方法是在类的实现文件中添加@synthesize指令。代码如下:
@interface Badger : NSObject ()
@property (nonatomic) Mushroom *mushroom;
@end
@implementation Badger
@synthesize mushroom = _mushroom;
- (Mushroom *)mushroom {
return _mushroom;
}
-(void)setMushroom: (Mushroom *)mush {
_mushroom = mush;
}
@synthesize指令会告诉编译器有一个叫做_mushroom的实例变量,它是mushroom以及setMushroom的实例变量。如果它不存在,就要将它创建出来。