• 继承(五)


    继承

    继承,是面向对象三大特征之一,继承的出现,是为了减少很多的冗余代码,因为是把各个类中,把相同特征和行为收集到另一个类中,然后这些类继承于这个集中类,可以把这个集中类的所有的特征和行为都继承过来。然后使用。

    说到类了,则应该提到子类和父类,这两者是同时存在的,不能说我是父类,也不能说我是子类,两者相互依存。谁是谁的父类,谁是谁的子类。

    子类继承父类,是单向的,两者之间不能相互继承。

    继承还具有传递性,用白话来说就是:B类如果继承于A类,C类继承于B类,此时B类就是A类的子类,C类就是B类的子类;A是B的父类,B是C的父类。这样的话,B(子类)就可以继承了A(父类)的所有实例变量和方法,这里说得是所有的实例变量和方法,也包括private修饰的实例变量,但是private修饰的实例变量,继承过来后不能直接使用。C(子类)继承了B(父类)的所有实例变量和方法,包括private修饰的实例变量。

    在OC中,所有的类的最终父类都是NSObject(这个类没有父类了,叫做根类)。

    子类继承过来的父类方法,可以重写。这里注意的是,1、重写父类方法,不需要再声明,就是不需要在.h文件里声明。2、重写父类方法,必须与父类方法名一样。

    一个练习:

    Animal.h:继承于NSObject

    #import <Foundation/Foundation.h>

    @interface Animal : NSObject{

        NSString *_kind;

        NSString *_color;

        @private

        NSString *_name;

    }

    - (void)sayHi;

     

    - (NSString *)kind;

    - (NSString *)color;

     

    - (void)setKind :(NSString *)kind;

    - (void)setColor :(NSString *)color;

    @end

    Animal.m

    #import "Animal.h"

    @implementation Animal

    - (void)sayHi{

        NSLog(@"动物乱叫...");

    }

    - (NSString *)kind{

        return _kind;

    }

    - (NSString *)color{

        return _color;

    }

    - (void)setKind :(NSString *)kind{

        _kind = kind;

    }

    - (void)setColor :(NSString *)color{

        _color = color;

    }

    @end

    Cat.h:继承于Animal类

    //继承的时候,使用#import引入父类头文件

    #import "Animal.h"

    @interface Cat : Animal{

    - (void)test;

    @end

    Cat.m

    #import "Cat.h"

    @implementation Cat

    - (void)test{

        _kind = @" ";

        _color = @" ";

    //    _name = @" ";

    }

    //重写父类继承过来的方法。

    //1、不用声明

    //2、方法名必须与父类的相同

    - (void)sayHi{

        NSLog(@"喵了个咪的..");

    }

     

    @end

    SmallCat.h:继承于Cat类

    #import "Cat.h"

    @interface SmallCat : Cat

    @end

    SmallCat.m

    #import "SmallCat.h"

    @implementation SmallCat

    @end

    main.m

    #import <Foundation/Foundation.h>

    #import "Cat.h"

    #import "SmallCat.h"

    int main(int argc, const char * argv[])

    {

        @autoreleasepool {

           

            Cat *c = [[Cat alloc]init];

            [c sayHi];//2015-04-15 10:47:46.528 OCLesson3_继承[951:41625] 动物乱叫...

            [c setKind:@"哈士奇.."];

            [c setColor:@"白色.."];

            NSLog(@"%@",c.kind);//2015-04-15 10:54:16.185 OCLesson3_继承[1045:44417] 哈士奇..

            NSLog(@"%@",c.color);//2015-04-15 10:54:16.186 OCLesson3_继承[1045:44417] 白色..

           

            c.kind = @"蓝猫..";

            c.color = @"白色的...";

           

            NSLog(@"%@",c.kind);//2015-04-15 10:54:16.186 OCLesson3_继承[1045:44417] 蓝猫..

            NSLog(@"%@",c.color);//2015-04-15 10:54:16.186 OCLesson3_继承[1045:44417] 白色的...

            [c sayHi];

           

            SmallCat *sc = [[SmallCat alloc] init];

            [sc sayHi];

          

        }

        return 0;

    }

    self:

    self是一个指向自己的指针(可以打印出来看地址),一般用在类内,用来调用自己的方法,当然这里得说明白,self如果在实例方法里,只能调实例方法,在类方法里只能调类方法,不允许在实例方法里调类方法,在类方法里调实例方法。

    self调自身的方法形式为:[self 本类方法名]。

    super:

    super 的出现,是因为在子类里,self调了自己重写父类的方法时,执行的是本类中的重写后的方法(若是本类中没有调用的方法,系统会自动去父类找,若是父类没有这个方法,就会去父类的父类找,一直找到NSObject根类,若是还没有,则程序崩溃),此时若是想执行父类的那个方法,那么就用到了super来调用父类的方法。

    self是指针,super不是指针,也不是变量,super是编译器的指令。专门用来访问父类的方法。若是在本类中使用super(将其当成变量赋值或者当成指针打印地址等),都会抱一个错误,叫做“未定义super”。

    super与self一样,在实例变量里只能调用实例方法,在类方法里只能调用类方法,不允许在实例方法里调类方法,在类方法里调实例方法。

    一个例子:

    Person.h:继承NSObject

    #import <Foundation/Foundation.h>

    @interface Person : NSObject

    - (void) sayHi;

    - (void)test;

    + (void)test2;

    + (void)test3;

    @end

    Person.m

    #import "Person.h"

    @implementation Person

    - (void) sayHi{

        NSLog(@"吃了吗?");

    }

    //测试self

    - (void)test{

        //self就是一个指向自己的指针。

        //self作用:在类内调用自己的方法。

        //self在实例方法中可以调用实例方法,

        NSLog(@"%p",self);//2015-04-15 14:50:24.972 OCLesson3_Self_Super[1733:91671] 0x100114040

        [self sayHi];

        //self在实例方法中,不能调用类方法。

    //    [self test2];

    }

     

    + (void)test2{

        //self在类方法中可以调用类方法。

        [self test3];

        //self在类方法中,不能调用实例方法。

    //    [self test];

    }

    + (void)test3{

    }

    @end

    Student.h:继承于Person

    #import "Person.h"

    @interface Student : Person

    - (void)stuTest;

    + (void)stuTest1;

    @end

    Student.m

    #import "Student.h"

    @implementation Student

    - (void)sayHi{

        NSLog(@"约吗?");

    }

     

    //测试super

    - (void)stuTest{

        //super可以调用父类的方法。

        //super是一条编译器指令(不是变量,也不是指针),用来访问父类方法。

    //    NSLog(@"%p",super);//报错super未定义

        [super sayHi];

        [super test];//实例方法里可以调实例方法。

    //    [super test2];//实例方法不能调类方法。

    }

     

    + (void)stuTest1{

        [super test2];

        [super test3];//super在类方法中调用类方法

    //    [super test];//super在类方法不能调用实例方法。

    }

    @end

    main.m

    #import <Foundation/Foundation.h>

    #import "Person.h"

    #import "Student.h"

    int main(int argc, const char * argv[])

    {

        @autoreleasepool {

    //        Person *p1 = [[Person alloc] init];

    //        NSLog(@"%p",p1);//2015-04-15 14:50:24.972 OCLesson3_Self_Super[1733:91671] 0x100114040

    ////        [p1 sayHi];

    //        [p1 test];//2015-04-15 14:49:07.067 OCLesson3_Self_Super[1710:90956] 吃了吗?

           

            Student *stu = [[Student alloc] init];

            [stu sayHi];//2015-04-15 15:04:35.318 OCLesson3_Self_Super[1798:96571] 约吗?

            [stu stuTest];//2015-04-15 15:05:43.319 OCLesson3_Self_Super[1811:97083] 吃了吗?

        }

        return 0;

    }

    完整的初始化方法:

    完整的初始化方法,要加上一个判断,就是遵循一个顺序,先对父类进行初始化,如果父类初始化成功后,就给子类返回一个地址,子类接收到地址后,再给自己初始化。如果在父类初始化的时候,失败了,就会给子类返回一个空值,子类接收到这个空值后,不再执行自己的初始化方法,而是把空值返回出去,让程序停止下来。

    上面实现过程代码如下:

    Person.h继承于NSObject

    #import <Foundation/Foundation.h>

     

    @interface Person : NSObject{

        NSString *_name;

        NSInteger _age;

    }

    - (instancetype)initWithName :(NSString *)name age:(NSInteger)age;

    @end

    Person.m

    #import "Person.h"

    @implementation Person

    - (instancetype)initWithName :(NSString *)name age:(NSInteger)age{

        //初始化从父类继承过来的东西。

        //1.初始化失败,返回值为nil(空)

        //2.初始化后会换地址,(NSMutableArray)类簇

        self = [super init];

        //一旦从父类继承的东西初始化失败,就不要再给自己的实例变量赋值了。

        if(self){

            _name = name;

            _age = age;

        }

        //如果初始化失败,返回nil

        //如果初始化成功返回地址。

         return self;

    }

    @end

    //指定初始化方法(课后找找)

    这里,先用super 调用父类(NSObject)的init初始化方法,然后用self指针来接收[super init]返回的地址。如果父类的初始化失败,返回一个空给self,当判断self的值,如果不为空,才为自己的类进行初始化,否则返回空出去。

    值得注意的是:[super init]指的是super调用的是父类的的初始化方法,不一定是init,也可能initXXX。(一般是继承之后再有一个子类继承它,才会出现这种状况)。

    一个例子:

    Student.h继承于Person

    自己声明了一个初始化方法,在实现初始化方法时,先用self接收[super initXXX ]父类初始化的结果,如果成功,返回地址后自己继续进行初始化。

    #import "Person.h"

    @interface Student : Person{

        NSString *_num;

    }

    - (instancetype)initWithName:(NSString *)name age:(NSInteger)age num:(NSString *)num;

    @end

    Student.m

    #import "Student.h"

    @implementation Student

    - (instancetype)initName:(NSString *)name age:(NSInteger)age num:(NSString *)num{

        self = [super initName:name age:age];

        if (self) {

            _num = num;

        }

        return self;

    }

    @end

    self = [super initName:name age:age];这一句就是说,super调了父类(Person)的初始化方法,如果成功,self接收地址,后继续执行_num = num;这个自己的初始化。

    相同的:

    CollageStudent.h继承于Student

    #import "Student.h"

    @interface CollageStudent : Student{

        NSString *_major;

    }

    - (instancetype)initName:(NSString *)name age:(NSInteger)age num:(NSString *)num major: (NSString *)major;

     

    @end

    CollageStudent.m

    #import "CollageStudent.h"

    @implementation CollageStudent

    - (instancetype)initName:(NSString *)name age:(NSInteger)age num:(NSString *)num major: (NSString *)major{

        self = [super initName:name age:age num:num];

        if (self) {

            _major = major;

        }

        return self;

    }

    @end

    都是一样的执行方式,一步一步往上,从最高的父类开始初始化,子类self接收,判断后才对自己初始化。

    便利构造器: 

    便利构造器是一个类方法,目的是为了方便使用者的使用,但是在写便利构造器的时候,会稍微麻烦。

    注意的是:

    1、便利构造器的出现,必须与对应的初始化方法一起出现;

    2、方法名是以类名开头(类名必须全部小写),后面跟着初始化方法init后面的全部东西。如:初始化方法是:

    - (instancetype ) initName :(NSString *)name age :(NSInteger)age;

    则便利构造器为:(Person类)

    + (instancetype)personName :(NSString *)name age :(NSInteger)age;

    便利构造器的实现,是把alloc这个内存分配的步骤放在构造器里,在main.m等里使用便利构造器的时候,不需要再alloc。

    例子:

    Person.h

    #import <Foundation/Foundation.h>

    @interface Person : NSObject{

        NSString *_name;

        NSInteger _age;

    }

    - (instancetype ) initName :(NSString *)name age :(NSInteger)age;

     

    //便利构造器(类方法)

    //写便利构造器前提要有一个对应的初始化方法

    //方法名以类名开头,类名小写,后面跟着init后面的所有东西。

    + (instancetype)personName :(NSString *)name age :(NSInteger)age;

     

    @end

    Person.m

    #import "Person.h"

    @implementation Person

    - (instancetype ) initName :(NSString *)name age :(NSInteger)age{

        self = [super init];

        if (self) {

            _name = name;

            _age = age;

        }

        return self;

    }

    //便利构造器实现

    + (instancetype)personName :(NSString *)name age :(NSInteger)age{

       

        Person *p = [[Person alloc]initWithName:name age:age];

        return p;

    }

    @end

    Person *p = [[Person alloc]initName:name age:age];这句的意思是,把本该在main里alloc的步骤,移到.m实现文件中。

    main.m

    #import <Foundation/Foundation.h>

    #import "Person.h"

    int main(int argc, const char * argv[])

    {

        @autoreleasepool {

            Person *p = [[Person alloc]initName:@"小黑" age:18];

            Person *p2 = [Person personName:@"小白" age:18];

        }

        return 0;

    }

    Person *p = [[Person alloc]initName:@"小黑" age:18]; 这个语句是直接调用初始化方法,需要alloc;

    Person *p2 = [Person personName:@"小白" age:18];这个语句是调用便利构造器,不需要alloc。相对于alloc,方便很多。

  • 相关阅读:
    C# 关键字 virtual、override和new的用法
    架构技术及架构要素总结【转】
    vue文件目录结构
    vue项目中,如何对static文件夹下的静态文件添加时间戳,以达到清除缓存
    webpack中关于require与import的区别
    vue 根据下拉框动态切换form的rule
    el-select 根据value查询其对应的label值
    web前端项目规范
    JavaScript 编码规范
    HTML 编码规范
  • 原文地址:https://www.cnblogs.com/DevinSMR/p/5118566.html
Copyright © 2020-2023  润新知