关于实例变量与属性的使用,其实一直以来我自己也很模糊,不知道什么情况下使用哪种方式会比较好。看了书之后对这个才有一些想法。
区别
1 直接访问实例变量,毫无疑问在速度上有一定优势,因为它不需要通过Objective-C的方法线程。编译器会直接通过内存来访问实例变量。
2 直接访问实例变量,就会绕开Setter或者Getter方法,这样会造成属性修饰符与实际操作的不同。比如属性用Copy修饰,采用直接访问的方式赋值的话,并不会产生Copy。
3 直接访问实例变量会造成KVO的失效。会不会造成问题取决于该属性是否有被观察。
4 使用Setter或者Getter可以帮助我们更好的Debug,这样我们就能在调试的时候通过打断点的方式来掌握属性被赋值或修改的时机。
选择
以上已经列出了各自方法的优势,关键还是如何选择性的书写。
一种比较合适的方式就是,分开书写。比如在读变量的时候,使用直接访问的方法,在给变量赋值的时候,使用Setter。
这样既可以快速的读取变量,同时又可以保留对属性赋值的控制权,便于Debug,也不会影响KVO,更重要的一点在于统一对属性修饰的支持,不会出现上文copy属性不起作用的问题。
注意事项
- 1 在inital方法中不要调用setter方法,原因可以举以下这个例子。
比如我有一个Teacher类,还有一个Teacher的子类JohnTeacher。
在父类Teacher中我们这样构造它的initWithName方法:
- (instancetype)initWithName:(NSString *)name
{
if (self = [super init]) {
self.name = [name copy];
}
return self;
}
在init中调用了self.name的Setter方法,如果此时我在子类中重写了这个Setter方法,比如在JohnTeacher中:
- (void)setName:(NSString *)name
{
if (![name isEqualToString:@"John"]) {
[NSException raise:NSInvalidArgumentException format:@"Name must be John"];
}
else
{
self.name = name;
}
}
这样的话,一旦我们调用[JohnTeacher alloc] initWithName:@"whatEver"];
那么都会走到子类的setName方法中,从而抛出异常。避免这样的情况就是在inital方法中不要使用属性的访问方法,而是直接使用下划线方法去进行变量修改。
不过如果在属性声明在父类中,那么在自己的initWithName方法中,就只能调用self.name = name 了。
还有一种需要使用属性访问方法的情况是属性使用了懒加载。否则属性变量不会被创建。
总结
1 无特殊情况下,通过直接访问的方式来读取属性实例变量。速度快,但是修改或者赋值的话还是使用访问方法来(也就是.语法 dot syntax)。
2 在init方法或者dealloc 方法中,直接访问实例变量。
3 当使用懒加载时,必须使用属性访问方法。