第6条:理解“属性”这一概念(下)

  方法名

  可通过如下特质来指定存取方法的方法名:

  • getter=<name> 指定“获取方法”的方法名。如果某属性是 Boolean 型,而你想为其获取方法加上 “is” 前缀,那么就可以用这个方法来来指定。比如说,在 UISwitch 类中,表示“开关”(switch)是否打开的属性就是这样定义的:
1 @property(nonatomic,getter=isOn) BOOL on;
  • setter=<name> 指定“设置方法”的方法名。这种用法不太常见。

  通过上述特质,可以微调由编译器所合成的存取方法。不过需要注意: 若是自己来实现这些存取方法,那么应该保证其具备相关属性所声明的特质。比方说,如果将某个属性声明为 copy,那么就应该在“设置方法”中拷贝相关对象,否则会误导该属性的使用者,而且,若是不遵从这一约定,还会令程序产生 bug。

  如果想在其他方法里设置属性值,那么同样要遵守属性定义中所宣称的语义。例如,我们扩产一下前面提到的 EOCPerson 类。由于字符串值可能改变,所以要把相关属性的“内存管理语义”声明为 copy。该类中新增了一个“初始化方法”(initializer),用于设置“名”(firstname)和“姓”(last name)的初始值:

1 @interface EOCPerson : NSManagedObject
2 
3 @property (copy) NSString *firstName;
4 @property (copy) NSString *lastName;
5 
6 - (instancetype)initWithFirstName:(NSString *)firstName
7                          lastName:(NSString *)lastName;
8 
9 @end

  在实现这个自定义的初始化方法时,一定要遵循属性定义中宣称的 “copy”语义,因为“属性定义”就相当于“类”和“待设置的属性值”之间所达成的契约。初始化方法的实现代码可以这样写:

 1 @implementation EOCPerson
 2 
 3 //@dynamic firstName, lastName;
 4 
 5 - (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName {
 6     if (self = [super init]) {
 7         firstName = [firstName copy];
 8         lastName = [lastName copy];
 9     }
10     return self;
11 }
12 @end

  读者也许会问: 为何不调用属性所对应的“设置方法”呢? 如果用了“设置方法”的话,不是总能保证准确的语义吗?(第七条会详细解释为什么决不应该在 init 或 dealloc )方法中调用存取方法。

  要是看过18条话,就会明白,应该尽量使用不可变的对象。如果将这一套用到 EOCPerson 类上,那就等于说,其两个属性应该设为“只读”。用初始化方法设置好属性值之后,就不能再改变了。再本例中,仍需要声明属性的“内存管理语义”。于是可以把属性的定义改成这样:

1 @property (copy, readonly) NSString *firstName;
2 @property (copy, readonly) NSString *lastName;

  由于是只读属性,所以编译器不会为其创建对应的“设置方法”,即便如此,我们还是要写上这些属性的语义,

以此表明初始化方法在设置这些属性值时所用的方式。要是不写明语义的话,该类的调用者就不知道初始化方法里会拷贝这些属性,他们有可能会在调用初始化方法之前自行拷贝属性值。这种操作是多余而且低效的。

  atomic 与 nonatomic 的区别是什么呢?前面说过,具备 atomic 特质的获取方法会通过锁定机制来确保其操作的原子性。这就是说,如果两个线程读写同一属性,那么不论何时,总能看到有效的属性值。若是不加锁的话(或者说使用 nonatomic 语义),那么当其中一个线程正在改写某属性值时,另外一个线程也许会突然闯入,把尚未修改好的属性值读取出来。发生这种情况时,线程读到的属性值可能不对。

  开发 iOS 程序,会发现,其中所有的属性都声明为 nonatomic。这样做的历史原因是:在 iOS 中使用同步锁的开销较大,就会带来性能问题。一般情况下并不要求属性必须是“原子的”,因为这并不能保证“线程安全”(thread safety),若要实现“线程安全”的操作,还需要采用更为深层的锁定机制才行。例如,一个线程在连续多次读取某属性值的过程中有别的线程在同时改写该值,那么即便将属性声明为 atomic,也还是会读取不同属性值。因此,开发 iOS 程序时一般都是会使用 nonatomic 属性。但是开发 Mac OS X程序时,使用 atomic 属性通常都不会有性能瓶颈。

END

posted @ 2017-06-18 02:16  鳄鱼不怕牙医不怕  阅读(247)  评论(0编辑  收藏  举报