34章 属性

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的实例变量。如果它不存在,就要将它创建出来。

你可能感兴趣的:(34章 属性)