从 C++ 到 Objective-C(21):隐式代码(续)

属性

使用属性

在定义类时有一个属性的概念。我们使用关键字@property来标记一个属性,告诉编译器自动生成访问代码。属性的主要意义在于节省开发代码量。

访问属性的语法比方法调用简单,因此即使我们需要编写代码时,我们也可以使用属性。访问属性同方法调用的性能是一样的,因为属性的使用在编译期实际就是换成了方法调用。大多数时候,属性用于封装成员变量。但是,我们也可以提供一个“假”的属性,看似是访问一个数据成员,但实际不是;换句话说,看起来像是从对象外部调用一个属性,但实际上其实现要比一个值的管理操作要复杂得多。

属性的描述

对属性的描述实际上是要告诉编译器如何生成访问器的代码:

  • 属性从外界是只读的吗?
  • 如果数据成员是原生类型,可选余地不大;如果是对象,那么使用 copy 封装的话,是要用强引用还是弱引用?
  • 属性是线程安全的吗?
  • 访问器的名字是什么?
  • 属性应该关联到哪一个数据成员?
  • 应该自动生成哪一个访问器,哪一个则留给开发人员?

我们需要两个步骤来回答这些问题:

  • 在类的@interface块中,属性的声明需要提供附属参数;
  • 在类的@implementation块中,访问器可以隐式生成,也可以指定一个实现。

属性访问器是有严格规定的:getter 要求必须返回所期望的类型(或者是相容类型);setter 必须返回void,并且只能有一个期望类型的参数。访问器的名字也是规定好的:对于数据foo,getter 的名字是foo,setter 的名字是setFoo:。当然,我们也可以指定自定义的名字,但是不同于前面所说的键值对编码,这个名字必须在编译期确定,因为属性的使用被设计成要和方法的直接调用一样的性能。因此,如果类型是不相容的,是不会有装箱机制的。

以下是带有注释的例子,先来有一个大体的了解。

@interface class Car : NSObject
{
    NSString* registration;
    Person* driver;
}

// registration 是只读的,使用 copy 设置
@property NSString*  (readonly, copy) registration;

// driver 使用弱引用(没有 retain),可以被修改
@property Person* (assign) driver;

@end
...
@implementation

// 开发者没有提供,由编译期生成 registration 的代码
@synthesize registration;

// 开发者提供了 driver 的 getter/setter 实现
@dynamic driver;

// 该方法将作为 @dynamic driver 的 getter
-(Person*) driver  {
    ...
}

// 该方法将作为 @dynamic driver 的 setter
-(void) setDriver:(Person*)value {
    ...
}
@end

属性的参数

属性的声明使用一下模板:

@property type name;

或者

@property(attributes) type name;

如果没有给出属性的参数,那么将使用默认值;否则将使用给出的参数值。这些参数值可以是:

  • readwrite(默认)或者 readonly:设置属性是可读写的(拥有 getter/setter)或是只读的(只有 getter);
  • assign(默认),retain 或 copy:设置属性的存储方式;
  • nonatomic:不生成线程安全的代码,默认是生成的(没有 atomic 关键字);
  • getter=…,setter=…:改变访问器默认的名字。

对于 setter,默认行为是assignretain或者copy用于数据成员被修改时的操作。在一个-(void) setFoo:(Foo*)value方法中,会因此生成三种不同的语句:

  • self->foo = value;: 简单赋值
  • self->foo = [value retain];: 赋值,同时引用计数器加 1
  • self->foo = [value copy];: 对象拷贝(必须满足协议NSCopying

在有垃圾收集器的环境下,retainassign没有区别,但是可以加上__weak或者__strong

@property(copy,getter=getS,setter=setF:) __weak NSString* s; // 复杂声明

注意不要忘记 setter 的冒号 : 。

2 Comments

  1. willonboy's blog 2011年4月7日
    • DevBean 2011年4月8日

Leave a Reply