4.4 属性
面向对象编程概念中,属性表示对象的特性,如名称、颜色、速度、尺寸、位置等。在Objective-C中,常用的属性定义方式有两种:
❑ 第一种是使用@proeprty和@synthesize指令快速创建,称为存储属性(stored property)。
❑ 另一种是使用setter和getter方法创建,称为计算属性(compute property)。
下面我们就分别介绍这两种创建属性的方式。
■4.4.1 使用@proeprty和@synthesize指令
在类中,使用@proeprty和@synthesize指令定义属性主要有两个步骤。
第一步,在类的接口部分使用@property指令声明属性的类型和名称,如下面的代码(CRobot.h文件)。
@interface CRobot : NSObject @property NSString* name; // 其他代码 @end
如果多个属性的类型是一样的,我们还可以使用一个@property指令同时声明,如下面的代码。
@property float xPos, yPos;
代码中同时定义了float类型的两个属性:x和y。
接下来,我们在类的实现部分使用@synthesize指令同步这些属性,如下面的代码。
@implementation CRobot @synthesize name; @synthesize xPos, yPos; // 其他代码 @end
请注意,在类的实现部分,使用@synthesize指令同步属性时,不再需要指定属性的类型;这样一来,我们可以将不同类型的属性写成一行,如下面的代码。
@synthesize name, xPos, yPos;
应用中,我们通过圆点运算符(.)来访问对象的属性,如下面的代码。
CRobot *robot5 = [[CRobot alloc] init]; robot5.name = @"No.5"; robot5.xPos = 5.0; robot5.yPos = 6.0; NSLog(@"机器人%@的位置在(%f, %f)", robot5.name, robot5.xPos, robot5.yPos);
使用@property和@synthesize指令创建属性的确非常方便,但也有一些不足,最明显的就是在设置属性值时,在类的内部无法对数据进行更多的操作,只能在设置属性值之前对数据进行处理,如正确性检查。
如果需要在设置属性值的同时,在类的内部可以对数据进行更多的处理,应使用getter和setter方法来创建属性。
■4.4.2 使用setter和getter方法
使用setter和getter方法创建类的属性时,一般会使用一个内部的实例变量来保存属性的数据,然后,我们会定义相应的方法设置和读取这个数据,如下面的代码,我们首先在类的接口部分声明一个属性(speed)的设置和读取方法。
@interface CRobot : NSObject -(float) speed; -(void) setSpeed:(float)s; // 其他代码 @end
接下来,我们会在类的实现部分定义属性数据实例变量,以及属性的设置和读取方法,如下面的代码。
@implementation CRobot { float _speed; } -(float) speed { return _speed; } -(void) setSpeed:(float)s { _speed = fabs(s); } // 其他代码 @end
在这个代码中,我们需要注意以下几个问题。
❑ 计算属性一般会在内部使用一个实例变量保存真正的数据,如代码中的_speed实例变量。
❑ getter方法用于获取属性值,其方法名也就是属性的名称,而它的实现也相对简单,一般来讲,直接返回对应的实例变量的数据就可以了。
❑ setter方法用于设置属性值,其命名规则是“set+属性名”,其中,属性名首字母大写。对于本例中的setter方法实现,使用了比较简单的处理方法,我们约定物体的速度不能是负数,所以,直接将传入数据的绝对值赋值给了_speed实例变量。这是特殊的处理方式,在开发工作中,我们可以根据需要对传入的数据进行检查和再加工。
实际上,游戏里角色的速度经常被设置为负数!为什么?后续内容将为您揭晓答案。
使用getter和setter方法定义的属性,同样可以使用圆点运算符(.),如下面的代码。
CRobot *robot5 = [[CRobot alloc] init]; robot5.name = @"五号"; robot5.speed = 50.0; NSLog(@"%@ 的速度是 %f km/h", robot5.name, robot5.speed);
此外,我们也可以看到,使用setter和getter方法定义的属性,其本质上还是方法,所以,我们也可以使用方法的形式来调用它们,但出于实际功能上的考虑,我们还是应该对属性和方法的应用加以区分。
关于属性,还有一个小秘密,使用@property和@synthesize指令创建的属性,同样可以使用方法的形式来操作,其原因是,在Objective-C的底层,属性是通过方法实现的,这是在编译阶段自动完成的。如下面的代码,我们使用方法的形式来访问name属性。
CRobot *robot5 = [[CRobot alloc] init]; [robot5 setName:@"No.5"]; NSLog(@"%@", [robot5 name]);