继承的基本概念:
现实生活中的继承:
人类是一个基类(也称做父类),通常情况下所有人类所共同具备的特性,如有手有脚能吃能喝
按照生活常规,我们继续给人类来细分的时候,我们可以分为学生类 工人类等,学生类和工人类同样具备手 脚 吃 喝等特性,而这些特性是所有人类所共有的,那么就可以让学生或工人类继承人类,这样当建立学生类和工人类的时候我们无需再定义人类中已经有的成员和方法,而只需要描述学生类和工人类所特有的特性即可。
学生类和工人类的特性是由在人类原有特性基础上增加而来的,那么学生类和工人类就是人类的派生类(也称做子类)。以此类推,层层递增, 这种子类获得父类特性的概念就是继承
OC中的继承关系
- B类继承A类,那么B类将拥有A类的所有属性和方法,此时我们说A类是B类的父类,B类是A类的子类
- C类继承B类,那么C类将拥有B类中的所有属性和方法,包括B类从A类中继承过来的属性和方法,此时我们说B类是C类的父类,C类是B类的子类
- 注意:
- 基类的私有属性能被继承,不能在子类中访问。
- OC中的继承是单继承:也就是说一个类只能一个父类,不能继承多个父类
- 子类与父类的关系也称为isA(是一个)关系,我们说 子类isA父类,也就是子类是一个父类,比如狗类继承动物类,那么我们说狗isA动物,也就是狗是一个动物。再如汽车继承交通工具,那么们说汽车isA交工工具,也就是汽车是一个交通工具
- 继承的合理性:引用《大话西游》里的一句话来描述继承的合理性。“人是人他妈生的,妖是妖他妈生的!” 以此类推 人妖是….
继承是代码重用的重要方式之一
- 来感受一下, 我们定义个学生类和一个工人类 如果不使用继承, 代码如下:
学生类声明文件.h:
#import <Foundation/Foundation.h> @interface Student : NSObject { // 姓名 NSString * _name; // 年龄 int _age; // 学号 int _studengNO; // 身份 NSString * _identity; } - (void) setName:(NSString *) name; - (NSString *) name; - (void) setAge: (int) age; - (int) age; - (void) setStudentNO: (int) studentNO; - (int) studentNO; - (void) setIdentity: (NSString *) identity; - (NSString *) identity; // 吃 - (void) eat; // 学习 - (void) study; @end
学生类实现文件.m:
#import "Student.h" @implementation Student - (void)setName:(NSString *)name{ _name = name; } - (NSString *)name{ return _name; } - (void)setAge:(int)age{ _age = age; } - (int)age{ return _age; } - (void)setStudentNO:(int)studentNO{ _studengNO = studentNO; } - (int)studentNO{ return _studengNO; } - (void)setIdentity:(NSString *)identity{ _identity = identity; } - (NSString *) identity{ return _identity; } // 吃 - (void)eat{ NSLog(@"吃东西"); } // 学习 - (void)study{ NSLog(@"学习"); } @end
工人类声明文件.h:
#import <Foundation/Foundation.h> @interface Worker : NSObject { // 姓名 NSString * _name; // 年龄 int _age; // 工号 int _workNO; // 身份 NSString * _identity; } - (void) setName: (NSString *) name; - (NSString *) name; - (void) setAge: (int) age; - (int) age; - (void) setWorkNO: (int) workNO; - (int) workNO; - (void) setIdentity: (NSString *) identity; - (NSString *) identity; // 吃 - (void) eat; // 学习 - (void) study; // 工作 - (void) work; @end
工人类实现文件:
#import "Worker.h" @implementation Worker - (void)setName:(NSString *)name{ _name = name; } - (NSString *)name{ return _name; } - (void)setAge:(int)age{ _age = age; } - (int)age{ return _age; } - (void)setWorkNO:(int)workNO{ _workNO = workNO; } - (int)workNO{ return _workNO; } - (void)setIdentity:(NSString *)identity{ _identity = identity; } - (NSString *) identity{ return _identity; } // 吃 - (void)eat{ NSLog(@"吃东西"); } // 学习 - (void)study{ NSLog(@"学习"); } // 工作 - (void)work{ NSLog(@"上班了 干活"); } @end
比较一下上面的代码 看看有多少是重复的:
声明文件中重复部分:
属性:
// 姓名 NSString * _name; // 年龄 int _age; // 身份 NSString * _identity;
行为:
- (void) setName:(NSString *) name; - (NSString *) name; - (void) setAge: (int) age; - (int) age; - (void) setIdentity: (NSString *) identity; - (NSString *) identity; // 吃 - (void) eat; // 学习 - (void) study;
实现文件中重复部分:
- (void)setName:(NSString *)name{ _name = name; } - (NSString *)name{ return _name; } - (void)setAge:(int)age{ _age = age; } - (int)age{ return _age; } - (void)setIdentity:(NSString *)identity{ _identity = identity; } - (NSString *) identity{ return _identity; } // 吃 - (void)eat{ NSLog(@"吃东西"); } // 学习 - (void)study{ NSLog(@"学习"); }
得到的结果是:
学生类中就一个学号在工人类中没有
工人类中就一个工号和一个工作的行为在学生类中没有
其余部分都是重复代码
如果还要添加几个其他类: 如学前期的幼儿类 退休后的老人类等等 那么上面那些重复的代码还得重复好几遍 复制粘贴都感觉累有木有
现在我们来添加一个Person类 人类中定义属性有 姓名 (name) 年龄(age) 身份(identity) 行为有 吃(eat) 学习(study)
使用继承关系后再来看看代码:
Person类声明文件:
#import <Foundation/Foundation.h> @interface Person : NSObject { // 姓名 NSString * _name; // 年龄 int _age; // 身份 NSString * _identity; } - (void) setName:(NSString *) name; - (NSString *) name; - (void) setAge: (int) age; - (int) age; - (void) setIdentity: (NSString *) identity; - (NSString *) identity; // 吃 - (void) eat; // 学习 - (void) study; @end
Person类实现文件:
#import "Person.h" @implementation Person - (void)setName:(NSString *)name{ _name = name; } - (NSString *)name{ return _name; } - (void)setAge:(int)age{ _age = age; } - (int)age{ return _age; } - (void)setIdentity:(NSString *)identity{ _identity = identity; } - (NSString *) identity{ return _identity; } // 吃 - (void)eat{ NSLog(@"吃东西"); } // 学习 - (void)study{ NSLog(@"学习"); } @end
学生类声明文件:
#import <Foundation/Foundation.h> #import "Person.h" @interface Student : Person { // 学号 int _studengNO; } - (void) setStudentNO: (int) studentNO; - (int) studentNO; @end
学生类实现文件:
#import "Student.h" @implementation Student - (void)setStudentNO:(int)studentNO{ _studengNO = studentNO; } - (int)studentNO{ return _studengNO; } @end
工人类的声明文件:
#import <Foundation/Foundation.h> #import "Person.h" @interface Worker : Person { // 工号 int _workNO; } - (void) setWorkNO: (int) workNO; - (int) workNO; // 工作 - (void) work; @end
工人类的实现文件:
#import "Worker.h" @implementation Worker - (void)setWorkNO:(int)workNO{ _workNO = workNO; } - (int)workNO{ return _workNO; } // 工作 - (void)work{ NSLog(@"上班了 干活"); } @end
好了, 现在再来看看代码: 我们的代码中多了一个Person类的声明和实现文件 在这个类中声明定义的就是我们之前学生类和工人类中重复的那一部分.
"B类继承A类,那么B类将拥有A类的所有属性和方法" 就是说如果 我们让学生类和工人类都继承自Person类 , 那么就算我们在学生类中和工作类中都没有声明那些姓名 年龄 吃 学习之类的属性和方法, 这两个类也能从Person类中得到这些行为属性. 而在学生类中只需要声明学生类特有的学号属性, 工人类中只需要声明工人类特有的工号,工作这种特有的行为属性即可, 这样子在学生类和工人类中得代码看起来简洁了很多, 而且以后如果我们需要再添加什么婴儿类 老人类, 也只需要在相关类中添加他们特有的行为属性就行了.
在这个例子里, Person类就是父类 学生类和工人类就是Person类的子类, 需要注意的是OC也是单继承的, 毕竟咱都只有一个父亲是吧, (呃 那个 干爹不算) , 在父类中定义的所有行为属性, 子类都能继承过去, 而在子类中还可以添加他特有的行为属性, 也就是说我爸有的 我都有, 我有的, 我爸不一定有(例如: 我会打Dota 我爸不会 ).
当我们发现两个类中有定义很多重复的行为属性的时候, 便可以把这些重复的部分都抽取出来当一个父类; 继承对代码的重用性和可扩展性 , 看上面例子体会一下吧
OC中继承的实现格式:
@interface 子类名称 : 父类名称 @end
记得导入父类头文件
继承:
当B类继承A类, 那么B类就拥有A类所有的属性和方法(类方法/对象方法)
优点:
提高代码的复用性
可以让类与类之间产生关系, 正是因为继承让类与类之间产生了关系所以才有了多态
缺点: 增加了代码之间的耦合性
注意:
不要以为继承可以提高代码的复用性, 以后但凡发现多个类当中有重复代码就抽取一个父类
只要满足一定的条件我们才能使用继承
条件: XXXX 是 XXX / 某某某 is a 某某某 (继承是 '是' 的关系 接口(协议) 是 '有' 的关系)
方法的重写:
- 在子类中实现与父类中同名的方法,称之为方法重写( 方法名相同 方法体不同 子类提供新的实现)
- 重写以后当给子类发送这个消息的时候,执行的是在子类中重写的那个方法,而不是父类中的方法
- 如果想在子类中调用被子类重写的父类的方法,可以通过super关键字
- 使用场景:当从父类继承的某个方法不适合子类,可以在子类中重写父类的这个方法
方法重写: 子类从父类继承了父类的方法(行为) 但是不想用父类的实现体 可以自己提供新的实现覆盖掉父类的实现
举例: 我爸会打人 我从我爸那也继承了打人的行为 我爸打人用拳头打 但是我感觉用拳头打不爽 就可以用自己的方式去打 (如用板砖砸) 这样虽然都是打人的方法 但是我用板砖打覆盖了从我爸那继承来的用拳头打 这样方法名相同 但是方法的实现子类和父类不同(子类的实现覆盖了父类的实现) 就是方法的重写
在上面例子中我们来重写父类学习的方法:
Person类中得学习方法实现:
// 学习 - (void)study{ NSLog(@"学习"); }
工人类重写父类的学习方法:
// 学习 - (void)study{ NSLog(@"我们在职人员通过上网来学习"); }
学生类重写父类的方法:
// 学习 - (void)study{ [super study]; NSLog(@"我们学生都通过去上课来学习"); }
继承中方法调用的顺序:
1、在自己类中找
2、如果没有,去父类中找
3、如果父类中没有,就去父类的父类中
4、如果父类的父类也没有,就还往上找,直到找到基类(NSObject)
5、如果NSObject都没有就报错了
以上如果找到了就执行这个方法,就不再往后查找了
继承的注意事项:
- 子类不能定义和父类同名的成员变量,私有成员变量也不可以;因为子类继承父类,子类将会拥有父类的所有成员变量,若在子类中定义父类同名成员变量 属于重复定义。
- OC类支持单一继承,不支持多继承;也就是说一个类只能有一个直接父类
- OC类支持多层继承
super关键字:
概念: super是个编译器的指令符号,只是告诉编译器在执行的时候,去调谁的方法
作用: 直接调用父类中的某个方法
super在对象方法中,那么就会调用父类的对象方法 super在类方法中,那么就会调用父类的类方法
使用场合: 子类重写父类的方法时想保留父类的一些行为