Objective-C @property 的声明与实现
属性的声明
属性的声明以关键字@property开始。该关键字可以出现在类的接口定义@interface中的方法列表中的任何地方。同时,@property还可以在协议或者是类别的声明中出现。
@property(attributes) type name;
其中的@property指令用来声明属性。其后可选的括号中的项是对属性的存储方式和其行为的细节描述。和其他的Objective-C中的类型一样,每一个属性都有一个类型修饰符和名称。
列表5-1展示了简单的属性的声明
@interface MyClass:NSObject { float value; } @proerty float value; @end
我们可以把属性的声明看作等同于声明了两个访问方法。因此
@property float value;
相当于:
-(float) value;
-(void) setValue:(float)newValue;
然而,属性的声明为如何实现对应的访问方法提供了更多的信息。(具体请参见“声明属性的属性”)。
属性的属性(Property Declaration Attributes)
译者注:Property和Attribute直译过来都是“属性”,为了避免混淆,本小节中分别使用Property和Attribute两个英文单词。
我们可以使用下面的形式来对Property进行修饰:
@property( attribute [, attribute2, ...])
和方法一样,Property的作用域局限于声明他的接口中。如果在声明Property时使用了逗号把多个Property的名称分隔开,那么其中的Attribute对所有Property都是适用的。
如果我们使用了@synthesize指令来告诉编译器自动创建访问方法,那么编译器创建的访问方法将是符合Attribute中的描述的。如果我们自己实现这些访问方法,我们必须保证自己的实现和Attribute中的描述是相一致的。(例如,如果我们在Attribute中指定了copy关键字,那么我们必须在实现setter的时候对输入的值进行copy赋值)。
访问方法的名称
和属性相关的getter和setter方法的缺省名称为:属性名称和set属性名称。例如,有属性,其名称为foo,那么缺省的getter方法的名称就是foo;setter方法的名称就为setFoo:。下面的attribute允许我们来自己定义getter和setter方法的名称。这些attribute都是可选的,并且几乎可以和其他的attribute一起出现(例外:readonly和setter=不能同时出现)。
getter=getterName
用于指定属性getter方法的名称。属性的getter方法的返回值必须和属性的类型是一样的,并且不能接收参数。
setter=setterName
由于指定属性的setter方法的名称。书香的setter方法必须接收一个和属性类型相同的参数,并且返回值必须是void的。如果属性被指定为是readonly(只读)的,那么指定其setter方法将会导致编译器报告错误。
可写性
下面的attributes将限定property是否有相关的setter方法。他们是互斥的。
readwrite
表示属性是可读写的。这个也是缺省的attribute。
在实现代码中,getter和setter都是要实现的。如果在实现代码中使用了@synthesize指令,那么编译器会自动生成getter和setter方法。
readonly
表示property是只读的。
如果使用了readonly attribute,那么在@impementation代码块中只需要实现getter方法。如果使用了@synthesize指令,那么也只有getter方法会被自动生成。如果使用点号运算符企图对其进行赋值,编译器会报错。
关于setter
下面的attributes表明了setter方法设置值时候的方法。他们是互斥的。
assign
表明只是简单的赋值。这个是缺省的。
通常只用于标量类型。例如,NSInteger和CGRect,或者是位置类型的对象,如delegetes。
retian和assign在支持垃圾回收环境中是等效的。
retain
表明在对象赋值的时候应该持有该对象。
之前的对象会被发送release消息。
在Mac OS X v10.6之前的版本中,这个attribute只是用于Objective-C的对象类型(也就是说不能向一个Core Foundation对象发送retian消息的)。
在Mac OS X v10.6及其之后的版本中,可以使用__attribute__关键字来表明Core Fundation属性应该按照Objective-C的对象来进行内存管理:
@property(retian) __attribute__((NSObject)) CFDictionaryRef myDictionary;
copy
表明应该使用对象的一个副本来进行赋值。
之前的对象会被发送release消息。
副本是通过向对象发送copy消息而得到的。这个attribute只适用于对象类型,并且要求该对象实现了NSCopying协议。更多信息请参见“拷贝”章节。
这些不同限制的使用和是否使用垃圾回收机制有关:
●如果没有使用垃圾回收机制,对于对象属性来说我们必须显示地指明是assign,retian还是copy。否则编译时候会报告错误或者警告(这种限制促使我们谨慎考虑自己到底需要使用什么样的内存管理机制并且显示地指定它)。
为了明确自己到底需要什么样子的内存管理方式,我们需要深入了解Cocoa的内存管理策略(参见《内存管理编程指南》一书)。
●如果使用了垃圾回收机制,使用缺省的attribute(也就是没有指定是assign,retian还是copy)不会导致编译告警。除非属性对应的类型遵守NSCopying协议。
缺省的attribute通常就是我们所需要的;如果属性的类型是可以复制的,为了达到封装的目的,通常我们都是会使用拷贝的副本。
原子性
我们可以使用下面的attribute来标明访问方法是非原子的:
nonatomic
该attribute标明访问方法是非原子的。缺省情况下访问方法是原子的。
缺省情况访问方法是原子的可以保证在多线程的环境中对属性的访问具有更好的鲁棒性。也就是说getter方法返回的值或者是setter方法设置值都是充分完好的,而不管多个线程是如何并发执行的。更多详细信息请参阅“性能和线程”章节。
如果我们使用了retain或者copy而没有使用nonatomic,那么在引用计数环境中,自动生成的getter方法中会先上锁,然后在持有原来的对象,再向对象发送autorelease方法,最后解锁并返回其值。这种实现类似如下:
[_internal lock] //使用对象级的锁
id result = [[value retian] autorelease];
[_internal unlock];
return result;
如果使用了nonatomic,自动生成的方法方法仅仅是简单直接地返回其值。