Objective-C 2.0 with Cocoa Foundation--- (继承 2)

来源:互联网 发布:gps端口检测 编辑:程序博客网 时间:2024/05/21 09:25

4.3,子类Subclass和超类Superclass

  让我们首先回忆一下第3章的Cattle.h,在Cattle.h里面我们有如下的代码片断:

@interface Cattle : NSobjecs {

  这段代码是在告诉编译器,我们的Cattle是继承的NSobjecs。在这段代码当中,NSobjecs是超类,Cattle是子类。通过这样写,我们曾经免费的得到了NSobjecs里面的一个方法叫做new。

    id cattle = [Cattle new];

  在面向对象的程序设计当中,如果在子类当中继承了超类的话,那么超类当中已经生效的部分代码在子类当中仍然是有效的,这样就大大的提高了代码的效率。基于超类我们可以把我们需要追加的一些功能放到子类里面去,在本章里面,我们决定基于Cattle类,重新生成一个子类Bull:

 1 #import 
  2 #import "Cattle.h"
  3 
  4 @interface Bull : Cattle {
  5     NSString *skinColor;
  6 }
  7 - (void)saySomething;
  8 - (NSString*) getSkinColor;
  9 - (void) setSkinColor:(NSString *) color;
10 @end

上段代码里面的第2行,是通知编译器,我们这个类的声明部分需要Cattle.h文件。这个文件我们已经很熟悉了,是我们在第3章曾经构筑过的,在本章里面,我们不会改变里面的任何内容。

  第4行,就是在通知编译器,我们需要声明一个类名字叫做Bull,从Cattle里面继承过来。

  第5行,我们追加了一个实例变量skinColor,用来保存Bull的颜色。

  第7行,我们重载了在Cattle类里面已经有的(void)saySomething实例方法。重载(void)saySomething方法的主要原因是,我们认为Bull说的话应该和Cattle有所区别。

  第8行到第9行,我们为Bull类声明了两个新的方法(NSString*) getSkinColor和(void) setSkinColor:(NSString *) color,分别用来设定和读取我们的实例变量skinColor。

  好的,我们总结一下继承的时候的子类的格式。

@interface 类的名字 : 父类的名字 {
     实体变量类型 实体变量名字;
    
}
- (返回值类型)重载的方法名字;
+ (返回值类型)重载的方法名字;
- (返回值类型)其他的方法名字:(变量类型) 变量名字:(变量类型) 变量名字;

@end

  4.4,self和super

  我们再来打开“Bull.m”, 在saySomething的定义的部分,我们发现了如下的代码:

    NSLog(@"Hello, I am a %@ bull, I have %d legs.", [self getSkinColor],legsCount);

  我们在这句话当中,发现的第一个新朋友是%@,这是在告诉编译器,需要把%@用一个后面定义的字符串来替换,在这里我们给编译器提供的字符串是[self getSkinColor]。看到这里,同学们又会发现一个新的朋友self。

  [对象或者类名字 方法名字:参数序列];

  在类的方法定义域里面,当我们需要调用类的其他方法的时候,我们需要指定对象或者类的名字,我们的方法是一个实例方法所以我们需要一个指向自己的对象,在这里我们需要使用self。

  我们假设,如果方法声明里面的参数序列里面有一个参数的名字和类的实例变量发生重复的情况下并且由于某种原因我们无法更改参数和实体变量的名字的话,我们应该如何应对呢?答案是使用self,格式如下

  self->变量名字

  通过这样写,我们可以取得到类的变量的数值。当然如果没有名字冲突的话,我们完全可以省略self->,Xcode也足够的聪明能够识别我们的实例变量,并且把我们代码里面的实例变量更改为相应的醒目的颜色。

  如果我们在类的方法里面需要访问超类的方法或者变量(当然是访问对子类来说是可视的方法或者变量),我们需要怎样写呢?答案是使用super,super在本质上也是id的指针,所以,使用super访问变量和方法的时候的书写格式,和self是完全一样的。

  “Bull.m”里面的其他的代码,没有什么新鲜的东西,所以笔者就不在这里赘述了。

  4.5,超类方法和子类方法的执行

  我们来看一下04-Hello Inheritance.m的下面的代码片断

1     id redBull = [Bull new];
2     [redBull setLegsCount:4];
3     [redBull setSkinColor:@"red"];
4     [redBull saySomething];
5     
6     Bull *blackBull = [Bull new];
7     [blackBull setLegsCount:4];
8     [blackBull setSkinColor:@"black"];
9     [blackBull saySomething];

  第1行的代码在第3章里面讲解过,我们来看看第2行的代码。

  第2行的代码实际上是向redBull发送一个setLegsCount消息,参数为4。我们没有在Bull里面定义setLegsCount方法,但是从控制台的输出上来看, setLegsCount明显是得到了执行。在执行的时候,我们给redBull发送setLegsCount消息的时候,runtime会在Bull的映射表当中寻找setLegsCount,由于我们没有定义所以runtime找不到的。runtime没有找到指定的方法的话,会接着需要Bull的超类,也就是Cattle。值得庆幸的是,runtime在Cattle里面找到了setLegsCount,所以就被执行了。由于runtime已经寻找到了目标的方法并且已经执行了,所以它就停止了寻找。我们假设runtime在Cattle里面也没有找到,那么它会接着在Cattle的超类NSobjecs里面寻找,如果还是找不到的话,由于NSobjecs是根类,所以它会报错的。关于具体内部是一个怎样的机制,我们将在后面的章节里面讲解。

  第3行的代码,是设定skinColor。

  第4行的代码是给redBull发送saySomething的消息。按照第2行的runtime的寻找逻辑,它首先会在Bull类里面寻找saySomething,这一次runtime很幸运,它一次就找到了,所以就立即执行。同时runtime也停止了寻找的过程,所以,Cattle的saySomething不会得到执行的。

  在第6行里面,我们定义了一个blackBull,但是这一次我们没有使用id作为blackBull的类型,我们使用了Bull *。从本质上来说,使用id还是Bull *是没有任何区别的。但是,我们来想象,当我们的程序存在很多id类型的变量的话,我们也许就难以区分究竟是什么类型的变量了。所以,在没有特殊的理由的情况之下,我们最好还是显式的写清楚类的名字,这样可以方便其他人阅读。由于Bull从Cattle继承而来,我们也可以把地6行代码改为

    Cattle *blackBull = [Bull new];