Objective-C和Sprite Kit游戏开发从入门到精通
上QQ阅读APP看书,第一时间看更新

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]);