6、理解“属性”这一概念
- “属性”(property)是Objective-C的一项特性,用于封装对象中的数据。Objective-C对象通常会把其所需要的数据保存为各种实例变量。实例变量一般通过“存取方法”(access method)来访问。“获取方法”(getter)读取变量值,“设置方法”(setter)写入变量值。
//这句代码等同于定义了一个名为_name的成员变量,声明并实现了它的setter方法以及getter方法
@property (nonatomic, copy) NSString *name;
- Objective-C把实例变量当做一种存储偏移量所用的“特殊变量”,交由“类对象”保管。偏移量会在运行时查找,如果类的定义变了,那么存储的偏移量也就变了,这样的话,无论何时访问实例变量,总能使用正确的偏移量。甚至可以再运行期向类中新增实例变量,这就是稳固的”应用程序二进制接口“(Application Binary Interface,ABI)。
属性特质
使用属性时要注意其各种特质,特质的设定也会影响编译器所生成的存取方法。比方下面这个属性就制定了三项特质:
@property (nonatomic, readwrite, copy) NSString *firstName;
属性可以拥有的特质分为四类:
①原子性:
在默认情况下,由编译器所合成的方法会通过锁定机制来确保其原子性(atomicity)。如果属性具备nonatomic特质,则不适用同步锁。请注意,尽管没有名为”atomic“的特质(如果某属性不具备nonatomic特质,那它就是”原子的“(atomic)),但是仍然可以在属性特质中写明这一点,编译器不会报错。若是自己定义存取方法,那么应该遵从与属性特质相符的原子性。
②读/写权限
- 具备readwrite(读写)特质的属性拥有getter与setter。若该属性由@synthesize实现,则编译器会自动生成这两个方法。
- 具备readonly(只读)特质的属性仅拥有getter方法。
③内存管理语义
- assign "setter"只会执行针对”纯量类型“(scalar type, 例如CGFloat或NSInteger等)的简单赋值操作。
- strong 此特质表明该属性定义了一种”拥有关系“(owing relationship)。为这种属性设置新值时,设置方法会先保留新值,并释放旧值,然后再讲新值设置上去。
- weak 此特质表明了该属性定义了一种”非拥有关系“(nonowing relationship)。为这种属性设置新值时,setter方法即不保留新值,也不释放旧值。此特质和assign类似,然而在属性所指的对象遭到摧毁时,属性值也会清空。
- copy 此特质所表达的所属关系与strong类似。然而设置方法并不保留新值,而是将其拷贝。当属性类型为NSString*时,经常用次特质来保护其封装性,因为传递给设置方法的新值有可能指向一个NSMutableString类的实例。这个类是NSString的子类,表示一种可以修改的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知道的情况下遭人更改。所以,这时就要拷贝一份不可变的字符串,确保对象中的字符串不会无意间变动。
④方法名
可通过如下特质来指定存取方法的方法名:
- getter=<name>指定”获取方法“的方法名。
- setter=<name>指定”设置方法“的方法名。这种用法不常见。
7、在对象内部尽量直接访问实例变量
写入实例变量时,通过setter方法来做,而在读取变量时,则直接访问。此方法技能提高读取操作的速度,又能控制对属性的写入操作。
使用点语法和直接访问变量两种方式访问变量有一下几个区别:
- 由于不经过Objective-C的”方法派发“步骤,所以直接访问实例变量的速度当然比较快。在这种情况下,编译器所生成的代码会直接访问保存对象实例变量的那块内存。
- 直接访问实例变量时,不会调用其setter方法,这就绕过了为相关属性所定义的”内存管理语义“。比方说,如果在ARC下直接访问一个声明为copy的属性,那么并不会拷贝该属性只会保留新值并释放旧值。
- 如果直接访问实例变量,那么不会触发”键值观测“(Key-Value Observing,KVO)通知。这样做是否会产生问题,还取决于具体的对象行为。
- 通过属性来访问有助于排查与之相关的错误。