Objective-C编程全解(第3版)3.2 利用继承定义新类_Objective-C编程全解(第3版)3.2 利用继承定义新类试读-查字典图书网
查字典图书网
当前位置: 查字典 > 图书网 > 编程 > Objective-C编程全解(第3版) > 3.2 利用继承定义新类

Objective-C编程全解(第3版)——3.2 利用继承定义新类

3.2.1 继承的定义 如果想通过继承为某个类定义一个子类,该怎么办呢? Objective-C 在子类的接口部分声明继承关系。在 2.2节中我们已经说明了如何定义类的接口,这 里再介绍一遍。 定义父类 A 的子类 B 的时候,“类名”是新类 B,冒号后面的“父类名”是需要继承的类 A。 至此为止本书中的父类都使用了 NSObject,这是因为 Objective-C中所有的类都要继承根类, 而 NSObject是 Objective-C中所有类的根类。如果子类有想继承的类,就要直接指明该类为父类,否 则就需要指定 NSObject 为父类。前文中定义 Volume 类的时候,因为 Volume 类并没有特别想继承的 类,所以直接使用了 NSObject 作为父类。 实例变量的声明中只需要声明新增的变量。如果没有新增的变量,则只需要加上 {} 即可,有时 甚至连 {} 都可以省略。 方法的声明中只需要追加新增的方法。如果要覆盖父类中已声明的方法(重写),则需要在接口 中对方法重新声明。通常我们会给重写的方法加上注释,以便理解。 下面展示了定义类 A 的子类 B 时接口部分的情况。变量 x 和方法 method1 继承于类 A,所以不 需要重新声明,方法 method2 的声明也可以被省略。 @interfaceB:A -(void)method2;//这个方法被覆盖了 @end 图 3-1 中定义类 C 时的接口部分如下所示,需要对变量 z 和方法 method3 进行声明。 @interfaceC:A { idz; } -(void)method3; @end 3.2.2 类定义和头文件 上一章介绍多文件编译时我们提到过接口部分通常都被声明为一个头文件,而这对继承来说也 是很重要的。 假设有一个已经定义好了的类 Alpha,那么头文件 Alpha.h 就应该已经存在。要定义类 Alpha 的 子类 Beta 的时候,头文件 Beta.h 中必须包含 Alpha.h。不知道父类定义的话是无法定义子类的。所以 包含父类接口的头文件是必须的。 类的实现部分必须引入包含类的接口部分的头文件。实现部分需要包含新增和重写的方法的实 现。当然实现部分也可以定义各种局部函数和变量。 图 3-3 的文件 Gamma.m 的方法中调用了方法doSomething,这个方法是从类 Alpha 继承而来 的。 文 件 Gamma.m 引 入 的 头 文 件 Gamma.h 中 引 入 了 Beta.h,Beta.h 中 又 引 入 了 Alpha.h, 所 以 Gamma.m 可以调用方法doSomething。 类的定义可以不断地使用继承向下扩展,但无论怎么扩展,只要保证了这种头文件的引入方式, 任何一个派生类中就都能使用父类中定义的变量和方法。 3.2.3 继承和方法调用 子类中定义的方法,除了能够访问新追加的实例变量外,也能够访问父类中定义的实例变量。 另外,因为继承的原因,子类也可以响应父类中定义的消息。但如果子类中重写了父类的方法, 就需要注意实际运行中到底哪个方法(父类的还是子类的)被执行了。 如图 3-4 所示,类 A 包含方法 method1、method2、method3。类 B 是类 A 的子类,类 B 中重新 定义了 method2。类 C 是类 B 的子类,类 C 中重新定义了 method1。 我们来看看给类 B 的实例变量发送消息时的情况。首先,假设向类 B 的实例对象发送了对应 method1 的消息,即进行了方法调用。虽然类 B 中没有 method1 的定义,但因为类 B 的父类类 A 中 定义了 method1,所以会找到类 A 的 method1,调用成功。消息 method3 的情况下也是同样的道理, 类 A 中定义的 method3 会被执行。method2 同前两个消息不同,类 B 中定义了 method2,所以会使 用自身定义的 method2 来响应这个消息。 而给类 C 的实例发送消息的话会怎么样呢?类 C 中有 method1 的定义,所以会直接使用类 C 中 定义的 method1 来响应这个消息。类 C 中没有 method2 的定义,所以调用的时候会使用类 B 中定义 的 method2 来响应。类 C 和类 B 中都没有定义 method3,所以类 A 中的定义 method3 会被调用。 3.2.4 调用父类的方法 子类继承了父类之后,有时就可能会希望调用父类的方法来执行子类中定义的其他处理,或者 根据情况进行和父类一样的处理或子类中单独定义的处理。让我们来看看图 3-4 中的例子,如果要在 类 B 的 method2 的定义中调用类 A 的 method2,那么该怎么办呢?通过 self 调用 method2 的话,就 会变成递归调用自身定义的 method2。 如果子类中想调用父类的方法,可以通过 super 关键字来发送消息。使用 super 发送消息后,就 会调用父类或父类的父类中定义的方法。如图 3-5 所示,类 C 中定义了 method1 和 method3。类 C 的 method1 中通过 super 调用了 method3,这时被调用的 method3 是类 A 中定义的 method3。 super 和 self 不同,并不确定指向某个对象。所以 super 只能被用于调用父类的方法,不能通过 super 完成赋值,也不能把方法的返回值指定为 super。 3.2.5 初始化方法的定义 新追加的实例变量有时需要被初始化。另外,子类也可能需要同父类不同的初始化方法。这些 情况下就需要为子类定义自己的初始化方法。 子类中重写 init 初始化方法的时候,通常按照以下逻辑。其他以 init 开头的初始化方法也是同理。 -(id)init { self=[superinit];/* 一定要在第一行调用父类的 init 方法 */ if(self!=nil){/* 父类返回了初始化好的实例时 */ .../* 子类专有的初始化操作 */ } returnself; } 请注意第一行调用了父类的init方法,父类的init方法会初始化父类中定义的实例变量。下 面是子类专有的初始化操作。 如果所有的类的初始化方法都这样写,那么根类 NSObject 的init方法就一定会被执行。否则 生成的对象就无法使用。与此同时,这样做也可以防止漏掉父类中定义的实例变量的初始化。 执行的时候父类的初始化方法可能会出错。出错时则会返回 nil,这种情况下子类也不需要再进 行初始化,直接返回 nil 就可以了。 如果父类是 NSObject,则基本上不可能初始化出错,因此不判断这个返回值也是可以的。使用 传入的参数或通过从文件读入变量进行初始化时,因为值的类型错误或读取文件失败等原因,初始 化有可能会失败。这种情况下,需要确认父类的初始化方法的返回值。另外,上例中对 self 进行了 赋值,关于这个赋值的含义我们会在第 8 章中详细说明,这里只需要记住这是初始化方法的一种固 定写法即可。 生成实例对象的方法alloc会把实例对象的变量都初始化为 0(后面会提到的实例变量 isa 除 外)。所以,如果子类中新追加的实例变量的初值可以为 0,则可以跳过子类的初始化。但是为了明确是否可以省略,最好为初值可为 0 的变量加上注释。 从程序的书写角度来说,设定初始值的方法有两种,即可以在初始化方法中一次性完成实例变量 的初始化,也可以在初始化方法中先设置实例变量为默认值,然后再调用别的方法来设置实例变量 的值。例如,类 Volume 也可以通过先调用初始化方法init,然后再调用setMax:等方法来设定音 量的最大值、最小值和变化幅度。原则上来说,初始赋值之后值不再发生变化的变量和需要显示设 定初值的变量,都需要通过带参数的初始化方法来进行初始化。

展开全文

推荐文章

猜你喜欢

附近的人在看

推荐阅读

拓展阅读

《Objective-C编程全解(第3版)》其他试读目录

• 3.1 继承的概念
• 3.2 利用继承定义新类 [当前]
• 3.3 使用继承的程序示例
• 3.4 继承和方法调用
• 3.5 方法定义时的注意事项
• 专栏:Objective-C 与开源软件
• 19.1 多线程
• 19.2 互斥