5.在对象内部尽量直接访问实例变量
1."通过属性访问"与"直接访问"实例变量的区别
- 由于不经过Objective-C的"方法派发"步骤,所以直接访问实例变量的速度比较快.在这种情况下,编译器说生成的代码会直接访问保存对象实例变量的那块内存
- 直接访问实例变量时,不会调用其"设置方法",这就绕过了为相关属性所定义的"内存管理语义".比如说,在ARC下直接访问一个声明为copy的属性,那么并不会拷贝属性,只会保留新值并释放旧值.
- 如果直接访问实例变量,那么不会触发"键值观测"(KVO)通知.这样做是否会产生问题,还取决于具体的对象行为.
- 通过属性来访问有助于排查与之相关的错误,因为可以给"获取方法"和/或"设置方法"中新增"断点",监控该属性的调用者及其访问时机.
2.解决方案
在写入实例变量时,通过其"设置方法"来做,而在读取实例变量时,则直接访问.此办法既能提高读取操作的速度,又能控制对属性的写入操作.之所以要通过"设置方法"来写入实例变量,其主要原因在于,这样做能够确保相关属性的"内存管理语义"得以贯彻.
3.注意
- 在初始化方法中应该直接访问实例变量,因为子类可能会"覆写"(override)设置方法.
EOCPerson.h
@interface EOCPerson : NSObject
@property (nonatomic,copy) NSString *firstName;
@property (nonatomic,copy) NSString *lastName;
- (id)initWithFirstName:(NSString *)firstName
lastName:(NSString *)lastName;
@end
EOCSmithPerson.m
@implementation EOCSmithPerson
- (void)setLastName:(NSString *)lastName {
if (![lastName isEqualToString:@"Smith"]) {
[NSException raise:NSInvalidArgumentException format:@"Last name must be Smith"];
}
self.lastName = lastName;
}
@end
EOCSmithPerson是EOCPerson的一个子类,表示姓Smith的人,在EOCSmithPerson中覆写了lastName的设置方法,如果此时在EOCPerson的默认初始化方法中使用属性访问实例变量,那么调用的将会是子类覆写的设置方法.如果我们我们将姓设置为了Smith之外的值时,这里将会进入if语句,抛出异常.
- 如果待初始化的实例变量声明在超类(父类)中,而我们又无法在子类中直接访问此实例变量的话,那么就需要调用设置方法了.
- 在使用"惰性初始化"(懒加载)时,必须通过"获取方法来访问属性.否则,实例变量永远不会初始化"
6.理解"对象等同性"这一概念
- 若想检测对象的等同性,需要提供"isEqual:"与hash方法.
- 相同的对象必须具有相同的哈希码,但是两个哈希码相同的对象却未必相同.
这句话描述的是hash码的特点,当我们重写hash方法,返回hash值时,要使返回值满足以上要求 - 不要盲目的检测每条属性,而是应该依照具体需求来制定检测方案.
例如,当一个类的实例是根据数据库中的数据生成的,这个对象的所有属性都是只读的,而且有一个属性是"唯一标识",代表数据库中的主键,这时我们判断这个类的实例是否相等时,就不用逐一判断所有属性是否相等,而是只判断"唯一标识"的属性就可以了 - 编写hash方法时,应该使用计算速度快而且hash码碰撞几率低的算法.
iOS 判断对象相等
网友评论