zoukankan      html  css  js  c++  java
  • Objective-C学习篇03—继承

    Objective-C学习篇03—继承

     

    大纲:

    继承的基本概念

    自定义初始化方法

    便利构造器方法

    重写description方法

    一 继承基本概念

      程序里的对象和"人类"的对象是一样的,高富帅继承了父母,自然就拥有了父母所有的资源,子类继承了父类同样就拥有了父类所有的属性和方法,当然,父类私有的除外.

      我们在定义一个新的类的时候,常常会遇到要定义的新类是某个类的扩展或者是某个类的修正等这种情况.如果能够在已有的类的基础上定义新类,那么新类的定义将会变得十分简便.

      类似于这种,通过扩展或者修改既有的类来定义新类的方法就叫做继承(inheritance).在继承关系中,被继承的类(原有的类)就成为父类(superclass),通过这种继承关系得到的新类就叫做子类(subclass).

      上面也讲到,继承意味着子类可以得到父类所有的属性和方法,除此之外,子类还可以为新类:

      1. 添加新的实例变量

      2. 追加新的自己特有的方法

      3. 重新定义父类中的方法

      当然,如果子类中只追加新的实例变量而不变更方法则没有任何意义.可以说,继承的目的就是要让子类能做更多父类做不了的事(扩展方法).子类中重新定义父类的方法叫做重写.分为完全重写和不完全重写,这些在后面都会讲到.

      继承的优点:1> 省略了大量重复的代码  2>建立起类与类之间的关系.

      缺点在于类间耦合性太强

    二 OC中的继承关系

      1. 在OC中,一旦建立了继承关系,子类就可以使用父类中所有的实例变量和方法.(父类私有的除外)

      2. OC中的继承是单继承关系,一个类只能有一个父类,但是一个类可以有n个子类.

      3. OC中的继承关系一定是合理的

      4. OC中继承是单根类继承,所有类的祖宗类都是NSObject,NSObject是所有类的基类或者根类

      5. A 继承自 B 可以说,A是B的子类,或者说A类是由B类衍生的,衍生类

      6. 使用继承的好处:如果一些类在定义的时候,发现他们有很多的实例变量或者方法是重复的(共同的特征),此时就可以把这些实例变量或者方法抽象出一个新的类作为这些类的父类,这样我们在定义一个类的时候,直接将要的定义的类继承自这个父类,就能省去很多重复的代码,只需要在子类中添加自己独有的特征和方法.

      7. 父类中被@public和@protected修饰的实例变量,子类中可以直接访问,而被@provate(私有的)修饰的实例变量子类不能直接访问,只能通过方法访问

      8. 子类在调用方法时优先去自己的类里面找,找到了就直接调用,找不到就去父类中找,找到就调用,找不到就直接向上找,直到最终找到NSObject,如果实现就调用,没有实现就Crash

      9. 重写父类的方法,一种是完全重写,不完全重写时需要在方法中使用super调用父类对方法的实现

     

      使用继承时要注意的问题

    1. OC中不允许子类和父类相同名称的实例变量,会引起混乱;
    2. OC中子类可以拥有和父类相同名称的方法,在子类调用时,优先去自己的内部找,找到就调用,没有就一层一层往上找.
    3. 子类的重写会覆盖掉父类以前的实现,因为子类重新实现了父类中的某个方法

      代码演示:
      首先,创建一个汽车Car类,让它继承自NSObject类

      Car.h

    复制代码
    @interface Car : NSObject   
    // 汽车的特征
    {
        @public
        NSString *_color; //颜色
    @protected NSString *_brand; //品牌
    @private NSInteger _wheel; //轮子 } // 车的行为 - (void)run; - (void)carShock;
    // 实例变量的setter getter方法 - (void)setColor:(NSString *)color; - (NSString *)color; - (void)setBrand:(NSString *)brand; - (NSString *)brand; - (void)setWheel:(NSInteger)wheel; - (NSInteger)wheel; @end
    复制代码

      Car.m

    复制代码
    @implementation Car
    - (void)run {
        NSLog(@"车在跑");
    }
    
    - (void)carShock {
        NSLog(@"车震是什么感觉,没试过");
    }
    
    - (void)setColor:(NSString *)color {
        _color = color;
    }
    - (NSString *)color { return _color; } - (void)setBrand:(NSString *)brand { _brand = brand; } - (NSString *)brand { return _brand; } - (void)setWheel:(NSInteger)wheel { _wheel = wheel; } - (NSInteger)wheel { return _wheel; } @end
    复制代码

      上面我们定义了一个汽车Car的类,,使它具有颜色,品牌,轮子的属性,具有跑和车震两种行为(方法).现在,我们给卡车Truck增加一个最大载货量的属性和输出自身信息的方法,同时又包含Car这些属性和方法,那么我们就要使用继承;

      这个时候利用继承的思想,我们只需要新建一个类,让它继承自Car这个类,新类中写自己特有的方法和属性.

      新建一个Truck类,继承自Car:

      Truck.h

    复制代码
    @interface Truck : Car
    {
        // NSString *_color; error 子类中不能定义和父类同名的实例变量
        CGFloat _maxLoad;   //最大载货量
    }
    
    - (void)setMaxLoad:(CGFloat)manLoad;
    - (CGFloat)maxLoad;
    
    // 描述卡车信息的方法
    - (void)describeTruck;
    - (void)run; // 子类中可以定义和父类同名的方法,这种形式叫做方法的重写//那么,为什么要进行方法的重写呢?因为父类提供的方法不能满足子类的需求// 重写父类的方法有两种,一种是完全重写父类的方法,另一种是不完全重写的方法

    @end
    复制代码

      Truck.m

    复制代码
    @implementation Truck
    // _maxLoad的setter getter方法
    - (void)setMaxLoad:(CGFloat)manLoad {
    
        _maxLoad = manLoad;
    }
    
    - (CGFloat)maxLoad {
        return _maxLoad;
    }
    
    
    // 描述卡车信息的方法
    - (void)describeTruck {
        NSLog(@"%@ %@ %ld %.2f", _color, _brand, self.wheel, _maxLoad);
    }
    // 注意:由于 _wheel被private修饰,不能在子类中直接访问,需要用self
    
    //  重写父类run的方法
    // 如果在重写父类方法时,没有调用父类的方法实现就叫做完全重写
    - (void)run
        NSLog(@"卡车在奔跑");
    }
    
    // 重写车震的方法
    - (void)carShock {
    // super 编译器指令,用于调用父类的方法,适用于想保留父类中某些方法的情况 [super carShock]; NSLog(@"卡车不好震"); } @end
    复制代码

    回到main中.

    复制代码
    // 创建一个大卡车的对象
    Truck *truc = [[Truck alloc] init];
    // color brand wheel是从父类继承过来的 truc.color = @"蓝色"; // 颜色 truc.brand = @"东风"; // 品牌 truc.wheel = 12; // 轮子 // maxLoad是子类特有的 truc.maxLoad = 120.0;// 载重量 // 调用父类的方法 // [truc carShock]; [truc run]; // 调用描述卡车的方法 [truc describeTruck];
    // 调用车震的方法 [truc carShock];
    复制代码

    打印结果:

    2015-11-26 22:46:44.741 OCLessonInherit-03[1759:364404] 卡车在奔跑

    2015-11-26 22:46:44.742 OCLessonInherit-03[1759:364404] 蓝色东风 12 120.00

    2015-11-26 22:46:44.742 OCLessonInherit-03[1759:364404] 没震过,不知道什么感觉

    2015-11-26 22:46:44.742 OCLessonInherit-03[1759:364404] 卡车不好震

      

    三 初始化方法

    1. 完整的初始化方法
    复制代码
    -  (id)init {
    // 执行父类中的init方法
        self = [super init]
    if (self) { // 初始化设置 _taxiMeter = @"计价器"; } // 返回初始化完成的对象 return self; }
    复制代码

    初始化过程:

             初始化时,先调用父类的[super init]方法(向上递归到NSObject类中的初始化方法),使用self接收结果,然后根据self中的值判断是否为nil,决定要不要初始自己的实例变量;如果为nil,说明父类的初始化方法失败了,就不再对自身实例变量初始化,如果不为nil,说明父类初始化方法成功了,再对自身实例变量进行初始化

      2.自定义初始化方法

      命名规范:

        1. 一定是对象方法,以减号 - 开头

        2. 返回值类型是 id 类型(最好写id类型) 或者是当前的类类型

        3. 必须以 initWith 开头

        4. 自定义初始化方法分为两种:完全自定义初始化 与 不完全初始化方法

       示例:

      不完全初始化方法:

    复制代码
    // 只初始化姓名和年龄
    - (id)initWithName:(NSString *)name age:(NSInteger)age {
    
        if (self = [super init]) {
            _name = name;
            _age = age;
        }
        return self;
    }
    复制代码

          完全自定义初始化:

    // 初始化全部的实例变量

    复制代码
    - (id)initWithName:(NSString *)name age:(NSInteger)age height:(CGFloat)height {
    if (self = [super init]) {
         _name = name; _age = age; _height = height; } return self; }
    复制代码

      3.便利构造器方法

      命名规范

      1. 遍历构造器方法一定是一个类方法,以加号开头(+)

      2. 返回值类型一定是 id类型

      3. 以类名小写开头 +With+实例变量名去掉下划线且首字母大写

      注意:

      4. 便利构造器又叫做工厂方法,用于快捷快速地创建对象

      5. 便利构造器方法的实质: 在方法内部把一个创建好的对象作为方法的返回值

      6. 便利构造器方法要和自定义初始化方法配套使用

     

      示例(对应上面列举的两种自定义初始化方法):

    复制代码
    + (id)personWithName:(NSString *)name age:(NSInteger)age {
    // 将一个创建好的对象作为便利构造器方法的返回值 return [[Person alloc] initWithName:name age:age]; }
    + (id)personWithName:(NSString *)name age:(NSInteger)age hight:(CGFloat)hight {
    return [[Person alloc] initWithName:name age:age height:hight]; } @end
    复制代码

      main

    复制代码
       Person *p = [[Person alloc]  init];
        NSLog(@"%@ - %ld - %.2f", p.name, p.age, p.height);

       //调用自定义初始化方法 Person *p1 = [[Person alloc] initWithName:@"吴邪"]; NSLog(@"%@ - %ld - %.2f", p1.name, p1.age, p1.height); Person *p2 = [[Person alloc] initWithName:@"花千骨" age:18]; NSLog(@"%@ - %ld - %.2f", p2.name, p2.age, p2.height);
      
       // 调用完全初始化方法 Person *p3 = [[Person alloc] initWithName:@"白子画" age:22 height:70]; NSLog(@"%@ - %ld - %.2f", p3.name, p3.age, p3.height); // 调用便利构造器方法 Person *p4 = [Person personWithName:@"杀阡陌"]; NSLog(@"%@ - %ld - %.2f", p4.name, p4.age, p4.height); Person *p5 = [Person personWithName:@"东方彧卿" age:15]; p5.name = @"糖宝"; p5.height = 0.3; NSLog(@"%@ - %ld - %.2f", p5.name, p5.age, p5.height); Person *p6 = [Person personWithName:@"单春秋" age:26 hight:1.65]; NSLog(@"%@ - %ld - %.2f", p6.name, p6.age, p6.height);
    复制代码

      以下是完整的代码:

      Person.h

    复制代码
    #import "Car.h"
    @interface Person : Car
    
    {
        NSString *_name;
        NSInteger _age;
        CGFloat _height;
    }
    
    - (void)setName:(NSString *)name;
    - (NSString *)name;
    
    - (void)setAge:(NSInteger)age;
    - (NSInteger)age;
    - (void)setHeight:(CGFloat)height; - (CGFloat)height; // 自定义初始化方法 - (id)initWithName:(NSString *)name; - (id)initWithName:(NSString *)name age:(NSInteger)age; - (id)initWithName:(NSString *)name age:(NSInteger)age height:(CGFloat)height; // 写一个遍历构造器的类方法 + (id)personWithName:(NSString *)name; + (id)personWithName:(NSString *)name age:(NSInteger)age; + (id)personWithName:(NSString *)name age:(NSInteger)age hight:(CGFloat)hight; @end
    复制代码
    复制代码
    #import "Person.h"
    @implementation Person
    - (void)setName:(NSString *)name {
        _name = name;
    }
    - (NSString *)name { return _name; } - (void)setAge:(NSInteger)age { _age = age; } - (NSInteger)age { return _age; } - (void)setHeight:(CGFloat)height { _height = height; } - (CGFloat)height { return _height; } // 自定义初始化方法 // 无论是哪种初始化方法,都要首先判断下父类有没有有初始化成功 - (id)initWithName:(NSString *)name { if (self = [super init]) { _name = name; } return self; } - (id)initWithName:(NSString *)name age:(NSInteger)age { if (self = [super init]) { _name = name; _age = age; } return self; } - (id)initWithName:(NSString *)name age:(NSInteger)age height:(CGFloat)height { if (self = [super init]) {
    _name = name; _age = age; _height = height; } return self; }
    + (id)personWithName:(NSString *)name {
    // 便利构造器的实质就是在便利构造器方法中创建一个对象,然后把这个对象作为返回值返回 // 遍历构造器方法一定要和自定义初始化方法配合使用 Person *p = [[Person alloc] initWithName:name]; return p; } + (id)personWithName:(NSString *)name age:(NSInteger)age { // 将一个创建好的对象作为便利构造器方法的返回值 return [[Person alloc] initWithName:name age:age]; } + (id)personWithName:(NSString *)name age:(NSInteger)age hight:(CGFloat)hight {
    return [[Person alloc] initWithName:name age:age height:hight]; } @end
    复制代码

     

    自己选的路,跪着也要走下去......
  • 相关阅读:
    根据指定月份,打印该月份所属的季节
    求出1~100之间,既是3又是7的倍数的自然数出现的次数
    打印所有的水仙花数
    升景坊单间短期出租
    找出1000以内的所有完数
    ssh config host
    shell获取ip
    mongodb sharding 简单部署记录
    tcp转发
    openssl和Java的keytool证书相关的命令总结
  • 原文地址:https://www.cnblogs.com/zmc815/p/5325815.html
Copyright © 2011-2022 走看看