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

4.2 类与对象

现在,我就要创建真正的机器人类型了。

与大多面向对象编程语言的区别在于,在Objective-C中并不使用class关键字来定义类,而是使用两个部分来定义类,包括接口(interface)部分和实现(implementation)部分。

理论上讲,我们可以将类的接口部分和实现部分都放在一个文件中,但我们一般会将接口部分定义在头文件(.h)中,而将实现部分定义在相应的模块文件(.m)中。

■4.2.1 接口部分

接下来的测试工作,我们将继续使用SimpleOC项目;在Xcode中,通过菜单“File”→“New”→“File”,选择OS X下的Source,然后选择Cocoa Class,接下来,需要我们指定类的名称(Class)及其基类(Subclass of),如图4-1所示。

图4-1 创建类

单击“Next”按钮,我们还需要指定代码保存的路径,然后,Xcode会自动为创建以类的名称命名的头文件和代码文件。首先,我们看一看头文件中的接口部分,如下面的代码(CRobot.h文件)。

    #ifndef __CRobot_h__
    #define __CRobot_h__
    #import <Foundation/Foundation.h>
    @interface CRobot : NSObject
    -(void) move;
    @end
    #endif

我们可以看到,类的接口部分定义在@interface和@end指令之间,而类的名称定义在@interface指令后面,紧跟其后的冒号(:)含义为继承,本例中,我们定义的CRobot类继承自NSObject类。

在CRobot类中,我们声明了一个名为move的实例方法,它没有返回值(使用void关键字声明),稍后,会看到更多关于方法的内容。接下来,我们先来看一看如何在实现部分定义这个方法。

■4.2.2 实现部分

下面的代码,我们将在CRobot.m文件中看到CRobot类成员的具体实现。

    #import "CRobot.h"
    @implementation CRobot
    -(void) move
    {
        NSLog(@"机器人移动");
    }
    @end

在这里,我们可以看到,类的实现部分定义在@implementation和@end指令之间。在@implementation指令后需要类的名称,但不需要再次指定继承哪个类。

在这里,可以看到move方法的实现代码,它的功能很简单,只是显示一条信息。

请注意,在类中的方法并没有使用小括号()来包含参数。实际上,其参数的定义方式与函数有所不同,稍后,我们会看到相关内容。

■4.2.3 创建对象(实例化)

下面的代码,我们演示了如何在main()函数中使用CRobot类。

    #import <Foundation/Foundation.h>
    #import "CRobot.h"
    int main(int argc, const char *argv[])
    {
        @autoreleasepool {
            CRobot *robot5 = [[CRobot alloc] init];
            [robot5 move];
    }
    return 0;
    }

代码中,我们需要使用#import指令引用CRobot.h文件。然后,在main()函数中,我们使用如下代码声明了一个CRobot类的实例,即robot5对象。

    CRobot *robot5

大家可以看到,对象是被定义为指针类型的,这就是加*的意义。

接下来,我们注意给robot5对象赋值的代码,它实际上完成了对象的实例化过程,这个过程共调用了两个方法,即alloc方法和init方法,也许大家会问,我们并没有定义这两个方法,它们是从哪里来的呢?答案就是,它们是从继承NSObject类而来的,也就是说,这两个方法是定义在NSObject类中的,由于CRobot是NSObject类的子类,所以,我们可以在CRobot类中使用这两个方法。

请注意,并不是基类中所有成员都能被子类访问的,这与类成员的访问级别有关,稍后我会讨论相关主题。

最后,我们调用了robot5对象的move方法,在Objective-C中,类或对象的方法调用,其基本格式如下。

    [<类或对象> <方法名和参数>];

调用类或对象中的方法时,需要使用一对方括号[]包含起来,如果你学习过C#或Java等编程语言,可能对这种方法的调用格式有些不适应,不过,用着用着也就习惯了。

■4.2.4 类的成员

前面的示例已经介绍了如何定义一个简单的类和方法,以及如何使用对象及其成员。实际开发中,类成员的定义会比前面的示例复杂得多,我们先来了解一些关于类成员定义和应用的基础知识。

1.属性和方法

定义一个类时,接口中定义的成员也就是类型对外公开的成员,主要包括属性和方法,属性用于定义对象的特性,而方法(任务)则用于定义对象可执行的动作。稍后,我们会详细讨论属性和方法的创建与使用。

2.实例方法和类方法

在前面定义的CRobot类中,我们定义的move属于实例方法,使用减号(-)定义。

    -(void) move;

实例方法的特点是,它必须由对象,即类的实例来调用。

另一种方法是类方法,它由类来调用,类方法使用加号(+)定义。

    +(void) methodName;

调用类方法时,直接使用类的名称。

    [CRobot methodName];

更多关于方法定义的内容稍后讨论。

3.实例变量

在类中,可以定义一些实例变量,这些变量可以在实例方法中调用,我们还可以通过@public、@private、@protected指令指定实例变量的适用范围(称为作用域或访问级别)。

我们可以在类的接口部分或实现部分定义实例变量,此时,应在紧跟接口或实现指令后的一对花括号{}之间,如下面的代码,我们在CRobot类的接口部分定义了counter实例变量。

    @interface CRobot : NSObject
    {
        int counter;
    }
    -(void) move;
    @end

在接口部分定义的实例变量,其默认使用范围是@protected(受保护的),这些实例变量可以在当前类或其子类的实例方法中使用。

实现部分定义的实例变量,只能用于当前类中的实例方法,相当于@private(私有的)访问级别。如下面的代码。

    @implementation CRobot
    {
        int counter;
    }
    // 其他代码
    @end

如果想简单点,还可以直接在接口部分定义全部的实例变量,并指定其访问级别,如下面的代码。

    @interface CRobot : NSObject
    {
    @private
        int counter = 0;
    @protected
        int x;
        int y;
    @public
        int identity;
    }
    -(void)move;
    @end

其中,counter变量为私有的实例变量,只能在本类中的实例方法中使用。x和y变量定义为受保护的实例变量,可以在本类或其子类的实例方法中使用,而identity变量则定义为公共的,可以由CRobot类型的对象使用→运算符调用,如下面的代码。

    CRobot *robot5 = [[CRobot alloc] init];
    robot5→identity = 5;
    NSLog(@"当前ID : %i", robot5→identity);

不过,在类中使用公共的实例变量并不是好的应用方式,如果我们需要使用对象的数据,可以将这个数据定义为属性。

4.访问级别

定义一个类时,有些成员是需要提供给外部代码调用的,而有些成员则只能在类的内部使用,此时,我们就应该考虑成员的访问级别问题。

一般来讲,我们将公共成员(属性和方法)声明在接口部分,然后在实现部分实现它们;而只限于本类或其子类使用的成员则应该定义在类的实现部分。