这种写法的问题是:对象布局在编译器(compile time)就已经固定了。碰到访问其中变量的代码,编译器就把它替换为“偏移量”(offset)。这个偏移量是硬编码(hardcode),表示该变量距离存放对象的内存区域的起始地址有多远。
这样子在修改了类定义后必须重新编译。否则offset是错误的。
例如某个代码库的代码使用了一份旧的类定义,如果和其相链接的代码使用了新的定义,那么运行时就会出现不兼容现象。
OC的做法,就是把实例变量当作一种存储偏移量所用的“特殊变量”,交由“类对象”保管。偏移量会在运行期查找,如果类的定义改变了,那么存储的偏移量也就变了。这样的话,无论何时访问实例变量,总能使用正确的偏移量。
甚至还可以在运行期间向类中新添实例变量。
这就是稳固的“应用程序二进制接口”(Application Binary Interface,ABI)。
2. 如果不想令编译器自动合成存取方法,则可以自己实现。如果只实现了其中一个存储方法,则另一个还是会由编译器来合成。
3. 使用 @dynamic 关键字可以阻止编译器自动合成。而且在编译访问属性的代码时,及时编译器发现定义的存取方法,也不会报错。
它相信这些在运行期能找到。
4. 原子性
默认情况下,由编译器所合成的方法会通过锁定机制确保其原子性(atomicity);
5. 读/写权限
① 具备 readwrite 特质的属性拥有“获取方法”和“设置方法”。@synthesize 会自动生成这两个方法。
② readonly 特质仅拥有获取方法。
6. 内存管理语义
属性用于封装数据,而数据则要有“具体的所有权语义”(concrete ownership semantic);
① assign:设置器置灰执行针对“纯量类型”的简单赋值操作。
② strong:设置新值时,会先保留新值,并释放旧值,然后再将新值设置上去。
③ weak:设置新值时,既不保留新值,也不释放旧值。当属性所指的对象遭到摧毁时,属性值也会清空。
④ unsafe_unretained:语义和assign想同,但适用于对象。该特质表达一种“非拥有”,当目标对象被摧毁,属性值不会自动清空,这点和weak不同。
⑤ copy:设置新值时不保留新值,将其“拷贝”。只要属性所用的对象是“可变的”(mutable),就应该在设置新属性值时拷贝一份。
7. 方法名
① getter = <name> 指定“获取器”的方法名。如果某属性时Boolean型,而想在其获取器上加“is”前缀。则可指定。
例如说有个属性这样定义:
@property (nonatomic, getter = isOn) BOOL on;
② setter = <name> 指定“设置方法”;
注:若是自己来实现这些存储方法,那么应该保证其具备相关属性所声明的特质。
例如声明特质为copy,则要在复制的时候使用copy。
决不应该在init方法(或者dealloc方法)中使用存取方法;
8. 在iOS中,使用同步锁的开销较大。一般情况下并不要求属性必须时原子的,因为这并不能保证“线程安全”,若要实现“线程安全”需采用更深层次的锁定机制才行。
Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法 对象属性 property,布布扣,bubuko.com
Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法 对象属性 property