Objective-C 基础教程第四章,继承

Objective-C 基础教程第四章,继承


//------------------------------------------------------------------------------
//OC 面向对象继承代码演示 ,矩形 圆形继承于Shape类
//
//《OC基础教程》 第四章第二小节
//------------------------------------------------------------------------------
#import <Foundation/Foundation.h>

//----------------------------------------变量声明--------------------------------
//几何体形状类型
typedef enum{
    kCircle,   //圆圈
    kRectangle,//矩形
    kEgg,      //鸡蛋
}ShapeType;
//几何体颜色类型
typedef enum{
    kRedColor,  //红色
    kGreenColor,//绿色
    kBlueColor, //蓝色
}ShapeColor;
//几何体轮廓结构体
typedef struct{
    int x,y,width,height;
}ShapeRect;
//-------------------------------------------------------------------------------

NSString *colorName(ShapeColor color)
{
    switch (color) {
        case kRedColor:
            return @"red";
            break;
        case kGreenColor:
            return @"green";
            break;
        case kBlueColor:
            return @"blue";
            break;
    }
    
    return @"no clue";
}

//================几何体类====================
/*几何体接口*/
@interface Shape : NSObject
{
    ShapeColor fillColor;
    ShapeRect  bounds;
}
- (void) setFillColor:(ShapeColor)fillColor;
- (void) setBounds:(ShapeRect)bounds;
- (void) draw;
@end
/*几何体实现*/
@implementation Shape

- (void) setFillColor:(ShapeColor)fillColor
{
    self->fillColor = fillColor;
}

- (void) setBounds:(ShapeRect)bounds
{
    self->bounds = bounds;
}

-(void) draw
{
    
}
@end
//================圆型类====================
/*圆型类接口,继承自Shape*/
@interface Circle : Shape
@end
/*圆型类实现,重写父类draw函数*/
@implementation Circle

- (void) draw
{
    NSLog(@"drawing a circle at (%d %d %d %d) in %@",
          bounds.x,bounds.y,
          bounds.width,bounds.height,
          colorName(fillColor));
}

@end

//================矩形类====================
/*矩形类接口,继承自Shape*/
@interface Rectangle : Shape
@end
/*矩形类实现,重写父类draw函数*/
@implementation Rectangle
- (void) draw
{
    NSLog(@"drawing a rectangle at (%d %d %d %d) in %@",
          bounds.x,bounds.y,//这里有个疑问,Rectangle类是如何能访问到bounds这变量的?
          bounds.width,bounds.height,//具体看本章的实例变量小节。
          colorName(fillColor));
}
@end
//===========================================




int main(int argc, const char * argv[]) {
    @autoreleasepool {
        id shapes[2];
        
        ShapeRect rect0 = { 0, 0, 10, 30 };
        shapes[0] = [Circle new];
        [shapes[0] setBounds: rect0];
        [shapes[0] setFillColor: kRedColor];
        [shapes[0] draw];
        
        ShapeRect rect1 = { 30, 40, 50, 60 };
        shapes[1] = [Rectangle new];
        [shapes[1] setBounds: rect1];
        [shapes[1] setFillColor: kBlueColor];
        [shapes[1] draw];
    }
    return 0;
}

继承的语法格式

和C++有点类似,OC继承的语法格式如下:

//圆形类接口声明  (圆形类继承自Shape 几何体类)
@interface Circle:Shape
@end

只不过需要注意的是OC中的继承无法用:进行多继承,比如如下的语法就是错误的。

//这样会导致编译错误
@interface Circle:Shape:NSObject 
@end

你可以通过Object-C的其他特性来达到多继承的效果,例如类别(category),后续第12章节将会学习到和协议(protocol)第13章节。

继承的工作机制

在如上代码中,我们对比第三章的代码进行了比较大的调整,提取了Circle和Rectangle的重复代码并将其加入Shape类中。

绝妙的是程序的其他部分不需要修改依然可以正常运行。

在此你会发OOP的一个强大地方:你可以对一个程序做一些重大的改变,如果你非常仔细,改变后程序仍然能正常运行。也就是重构

方法调度

当执行[shape setFillColor:RedColor];代码的时候,OC的方法调度机制首先会寻找接收消息的对象,在当前例子中的对象是shape他是Circle类的对象,该对象拥有一个当前类的this指针,在OC中他是self指针。通过该指针来寻找他执行的代码,比如我们调用这句代码的时候self指针就会找到setFillColor这代码。

image-20220205111703424

那么当我们有继承属性的时候,可以从如下图中看到shape对象还有一个指向其SuperClass(超类)的指针。

当我们指向[shape setFillColor:RedColor];代码的时候,调度程序先询问Circle类是否能响应setFillColor:消息,看如下图Circle Class没有声明定义setFillColor方法,所以调度程序会继续询问其SuperClass(超类)是否能响应其方法,结果是其父类能够响应该消息。

image-20220205112620951

实例变量

👌🏻好的,到这里我们已经知道了方法是如何响应调用消息的,下面我们将学习Objective-C如何访问实例变量,以及circle类的draw方法如何找到Shape类中声明的bounds和fillColor变量

//为了好理解,我们来新建一个形状
@interface RoundedRectangle : Shape //[继承自Shape类]
{
  @private
    int radius;
}

image-20220223222847730

首先我们根据新添加的代码,然后再看上图可以从中得知,新建一个类时,其对象首先会从它的超类继承实例变量,然后根据自身情况添加自己的实例变量,可以看到最顶上的变量也是继承最父级的变量isafillColor|boundsradius

补充:isa是NSObject的实例变量,其变量名isa来源是因为(子类和超类之间建立了 是一个(is a)的关系),所以就用了这个名字,也就是RoundedRectangle这个类 是一个(isa) Shape。

self

之前讲过self,在每个方法调用都有一个self的隐藏参数,它是一个指向接收消息对象指针,这些方法通过self参数来寻找他们需要用到的实例变量。

下图所示:RoundedRectangle对象的self参数指向了isa,他通过isa+偏移地址,然后不断的找到了继承的每个实例变量,所以这里我们就可以回答之前的问题了,Object-C如何访问实例变量,如何找到Shape类中声明的bounds变量和fillColor变量。

image-20220224221914143

重写方法

@implementation Circle
-(void) setFillColor:(ShapeColor) c
{
  if(c == KredColor) //如果是红色
  {
    c = kGreenColor;//将其改为绿色
  }
  
  //继续调用父类的方法,这样我们即重写了父类的setFillColor方法添加了新特性,又保留了父类的旧方法还能在此时调用。
  [super setFillColor:c];
}
@end

重写方法可以让我们在新创建的类里面添加自己的新特性,比如上面代码我们给Circle添加了把红色替换成绿色的代码,从而我们还能调用父类的setFillColor方法直接将颜色生效,这样省的我们添加生效颜色的代码了,在添加新特性的同时还保留了旧的代码。

小结

继承是一个非常重要的概念,OOP中很多高级技术都涉及到继承。在本章节中,我们学习了与继承有关的概念以及用代码和图的方式来更加具体生动的了解这些知识,讨论了通过现有类构造新类,如何将超类实例变量引用到子类中,方法调度机制self和重写方法等知识。

posted @ 2022-02-24 23:26  VxerLee昵称已被使用  阅读(131)  评论(0编辑  收藏  举报