用Objective-C等面向对象语言编程时,“对象”(object)就是“基本构造单元”(building block),开发者可以通过对象来存储并传递数据。在对象之间传递数据并执行任务的过程就叫做“消息传递”(Messaging)。
“属性”(property)是Objecive-C的一项特性,用于封装对象中的数据。
当应用程序运行起来以后,为其提供相关支持的代码叫做“Objective-C运行期环境”(Objective-C runtime),它提供了一些使得对象之间能够传递消息的重要函数,并且包含创建类实例所用的全部逻辑。
如果使用了属性的话,那么编译器就会自动编写访问这些属性所需的方法,此过程叫做“自动合成”(autosynthesis)。需要强调的是,这个过程由 编译器在编译期执行,所以编辑器里看不到这些“合成方法”(synthesized method)的源代码。除了生成方法代码之外,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前面加下划线,以此作为实例变量的名字。
若不想令编译器自动合成存取方法,则可以自己实现。如果你只实现了其中一个存取方法,那么另外一个还是会由编译器来合成。还有一种办法能阻止编译器自动合 成存取方法,就是使用@dynamic关键字,它会告诉编译器:不要自动创建实现属性所用的实例变量,也不要为其创建存取方法。而且,在编译访问属性的代 码时,即使编译器发现没有定义存取方法,也不会报错,它相信这些方法能在运行期找到。比方说,如果从CoreData框架中的 NSManagedObject类里继承了一个子类,那么就需要在运行期动态创建存取方法。继承NSManagedObject时之所以要这样做,是因为 子类的某些属性不是实例变量,其数据来自后端的数据库中。例如:
@interface EOCPerson : NSManagedObject
@property NSString *firstName;
@property NSString *lastName;
@end @implementation EOCPerson
@dynamic firstName, lastName;
@end
属性可以拥有的特质分为四类:
原子性
在默认情况下,由编译器所合成的方法会通过锁定机制确保其原子性(atomicity)。如果属性具备nonatomic特质,则不使用同步锁。请 注意,尽管没有名为“atomic”的特质(如果某属性不具备nonatomic特质,那它就是“原子的”(atomic)),但是仍然可以在属性特质中 写明这一点,编译器不会报错。若是自己定义存取方法,那么就应该遵从与属性特质相符的原子性。
atomic与nonatomic的区别是什么呢?前面说过,具备atomic特质的获取方法会通过锁定机制来确保其操作的原子性。这也就是说,如果两个 线程读写同一属性,那么不论何时,总能看到有效的属性值。若是不加锁的话(或者说使用nonatomic语义),那么当其中一个线程正在改写某属性值时, 另外一个线程也许会突然闯入,把尚未修改好的属性值读取出来。发生这种情况时,线程读到的属性值可能不对。
开发iOS程序时应该使用nonatomic属性,因为atomic属性会严重影响性能。
读/写权限
具备readwrite(读写)特质的属性拥有“获取方法”(getter)与“设置方法”(setter)。若该属性由@synthesize实现,则编译器会自动生成这两个方法。
具备readonly(只读)特质的属性仅拥有获取方法,只有当该属性由@synthesize实现时,编译器才会为其合成获取方法。你可以用此特 质把某个属性对外公开为只读属性,然后在“class-continuation分类”中将其重新定义为读写属性。
内存管理语义
属性用于封装数据,而数据则要有“具体的所有权语义”(concrete ownership semantic)。下面这一组特质仅会影响“设置方法”。例如,用“设置方法”设定一个新值时,它是应该“保留”(retain)此值呢,还是只将其赋 给底层实例变量就好?编译器在合成存取方法时,要根据此特质来决定所生成的代码。如果自己编写存取方法,那么就必须同有关属性所具备的特质相符。
assign “设置方法”只会执行针对“纯量类型”(scalar type,例如CGFloat或NSInteger等)的简单赋值操作。
strong 此特质表明该属性定义了一种“拥有关系”(owning relationship)。为这种属性设置新值时,设置方法会先保留新值,并释放旧值,然后再将新值设置上去。
weak 此特质表明该属性定义了一种“非拥有关系”(nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似,然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)。
unsafe_unretained 此特质的语义和assign相同,但是它适用于“对象类型”(object type),该特质表达一种“非拥有关系”(“不保留”,unretained),当目标对象遭到摧毁时,属性值不会自动清空(“不安 全”,unsafe),这一点与weak有区别。
copy 此特质所表达的所属关系与strong类似。然而设置方法并不保留新值,而是将其“拷贝”(copy)。当属性类型为NSString* 时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个NSMutableString类的实例。这个类是NSString的子类,表 示一种可以修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不 可变”(immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的”(mutable),就应该在设置新属 性值时拷贝一份。
方法名
可通过如下特质来指定存取方法的方法名:
getter=<name> 指定“获取方法”的方法名。如果某属性是Boolean型,而你想为其获取方法加上“is”前缀,那么就可以用这个办法来指定。比如说,在UISwitch类中,表示“开关”(switch)是否打开的属性就是这样定义的: @property (nonatomic, getter=isOn) BOOL on;
setter=<name> 指定“设置方法”的方法名。这种用法不太常见。