继承(五)

继承

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

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

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

继承还具有传递性,用白话来说就是: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,方便很多。

原文地址:https://www.cnblogs.com/DevinSMR/p/5118566.html