用OC等面向对象语言编程时,“对象”就是“基本构造单元”,在对象之间传递数据并执行任务的过程就叫做“消息传递”。需要熟悉这两个特性的工作原理。
属性
实例变量存取
实例变量一般通过“存取方法”来访问,其中“获取方法”(getter)用于读取变量值,“设置方法”(setter)用于写入变量值。是OC 2.0里的“属性”概念。
点语法是为了调用属性。
@interface EOCPerson : NSObject {
@public
NSString* _firstName;
NSString* _lastName;
@private
NSString* _someInternalData;
}
问题:对于OC而言,对象布局在编译器就已经很稳固了,只要碰到访问_firstName变量的代码,编译器就把其替换为“偏移量”,这个偏移量就是硬编码,表示该变量距离存放对象的内存区域的起始地址有多远。若又加了一个实例变量,那就麻烦了。
@interface EOCPerson :NSObject {
@public
NSDate* _dateOfBirth;
...
}
原来指向_firstName的偏移量现在却指向了_dateOfBirth。运行期出现不一致的现象。
OC对于处理此种问题,做法是吧实例变量当做一种存储偏移量所用的“特殊变量”,交由“类对象”保管。偏移量会在运行期查找,如果类的定义变了,那么存储的偏移量也变化了,无论何时访问实例变量,总能访问到正确的偏移量。这就是稳固的“应用程序二进制接口”(ABI)。
属性存取
@interface EOCPerson : NSObject
@property NSString* firstName;
@property NSString* lastName;
@end
如果要访问属性,可以使用点语法
例如
self.firstName 点语法调用属性
_firstName 直接调用实例变量(类内部)
属性有很多优势,如果使用了属性的话,那么编译器就会自动编写访问这些属性所需的方法,此过程叫做“自动合成”。除了生成方法代码外,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前加下划线(_XX),以此作为实例变量的名字。也可以在实现文件里通过@synthesize语法来制定实例变量:
@implementation EOCPerson
@synthesize firstName = _firstName;
@synthesize lastName = _lastName;
@end
如果阻止编译器自动合成存取方法,就是使用@dynamic关键字。
@implementation EOCPerson
@dynamic firstName, lastName;
@end
属性特质(attribute)
注意:若是自己定义存取方法,那么就应该遵从与属性特质相符的语义
- 原子性
原子性:atomic(编译器所生成的方法会通过锁定机制确保其原子性)
非原子性:nonatomic
开发iOS程序,所有的属性都声明为nonatomic,原因:iOS使用同步锁的开销比较大,这会带来性能问题,一般情况下并不要属性必须是“原子的”,因为这并不能保证“线程安全”,如果实现“线程安全”的操作,还需采用更为深层的锁定机制才可以。
- 读/写权限
readwrite:获取方法 与 设置方法
readonly:获取方法
- 内存管理语义
assign:针对“纯量类型(scalar type)”(例如NSInteger,BOOL)的简单复制操作。
strong:针对“拥有关系(owning relationship),为这种属性设置新值时,设置方法会先保留新值,并释放旧值,然后将新值复制上去。
weak:针对“非拥有关系”(nonowning relationship),为这种属性设置新值时,设置方法既不保留新值,也不释放旧值,不同于assign的是当属性所指的对象遭到摧毁时,属性值也会被清空(nil)。
unsafe_unretained:语义同assign,但是它适用于对象类型,针对“非拥有关系(unretained)”,当目标遭到摧毁时,属性值不会自动清空。
copy:设置方法并不保留新值,而是将其“copy”。多用于NSString类型。
- 方法名
getter=:指定 获取方法 的方法名。
setter=:制定 设置方法 的方法名。
如果要再设置属性所对应的实例变量时,一定要遵循该属性所声明的语义