继承的理解
继承从生活逻辑上理解是继承者从被继承者身上获得一些已有的信息, 财产等
在面向对象中也一样, 继承是子类从父类中获得父类的全部实例变量和方法
1. 继承的优点:
* 减少代码量, 提高工作效率
解释: 子类从父类中获得父类的实例变量和方法, 这时就不需要在子类中再写和父类中出现的代码了, 节省了所有父类中的全部代码.
(父类中@private 声明的实例变量, 在子类中也有, 但是只能通过父类的方法访问它. 子类自身定义的方法不能访问继承自父类的@private修饰的实例变量)
继承的特点:
1.继承是单向(从上到下)
2.继承具有传递性: A继承于B, B继承与C, A具有B和C的特征和行为
3.不能相互继承.
4.不能多继承
5.子类能继承父类全部的特征和行为(实例变量和方法).
需要注意的是, OC中只支持单继承. 既子类只能继承一个父类, 不能同是继承多个父类.
也就是说基本上就不要有类似于定义一个类继承所有类就可以完成所有功能的想法了. 这样是为了保证逻辑上的独立性, 比如杜绝了动物类继承了植物类的特点 (p.s.总不能让一个动物类通过继承既获得了动物的特性, 又获得了植物光合作用的功能吧, 这从生活逻辑上来说相当于动植物杂交生出一个动物类). 像这样动物类只能继承动物类, 保证了一个事物的基本特征不变, 避免了胡乱继承带来的逻辑混乱p.s. 在代码世界中, 可以定义一个类随意继承一个类, 比如让一个猫类继承了植物类. 但是这样会导致这个猫类的逻辑混乱(它到底是一个Cat还是一个植物?). 在用一个类继承另一个类的时候, 程序员也要考虑到其中的逻辑性.
关键字: self 和 super
self 和 super没有特殊的意义, 都是一个编译器的指令
在本类中调用本类的对象方法 用[self 方法名]的方式,
调用父类的对象方法 用[super 方法名]的方式
- 用self调用方法, 会在本类中搜索被调用的方法, 若本类中没有, 则继续寻找父类中的方法.
- 用super会直接跳过本类 调用父类的方法, 若父类中没有 再调用父类的父类中的方法.
- 区别就是 self和super开始找方法的起点不同, 一个在本类, 一个在父类开始. 查找方法相同
总结一下 继承中方法的调用顺序:
使用self的时候
- 优先调用本类中的方法
找到就调用, 找不到就到父类中找 一层层的往上找. 找到就调用 然后停止寻找, 一直都没有找到就会崩溃
使用super的时候
- 直接跳过本类 找上一级的父类, 若找到就调用, 停止寻找. 找不到就继续找父类的父类直到找到为止. 没找到就崩溃
关于初始化方法
在子类中写自定义初始化方法
由于可能存在继承失败的情况
所以在本类初始化的时候, 一部分参数是继承自父类方法的
如果在继承失败的时候, 就有可能导致本类中继承自父类的实例变量没有被创建 也就没法被赋值.
知乎网友@方秋枋 总结了一个回答
[super init]的作用和赋值给self的意义
为什么要把初始化好的超类(父类)赋给self?
我们需要知道面向对象继承的概念,一个子类从父类继承,获得相关的属性和方法,所以在子类的初始化方法中,必须首先调用父类的初始化方法,以实现父类相关资源的初始化。网上有网友总结的很好:- [super init]的作用:面向对象的体现,先利用父类的init方法为子类实例的父类部分属性初始化。
- self 为什么要赋值为[super init]:简单来说是为了防止父类的初始化方法release掉了self指向的空间并重新alloc了一块空间。这时的话,[super init]可能alloc失败,这时就不再执行if中的语句。
把self指向超类以后,self用点语法调用方法不就直接调用超类(父类)的方法了吗?
- 同样也是面向对象的概念,调用的方法如果是继承自父类的,的确是调用了父类的方法的代码,如果是自己新添加的方法,则调用是原原本本自己的方法。
具体例子如下题
父类定义声明如下:
@interface Person : NSObject { NSString *_name; // 姓名 NSString *_gender; // 性别 NSString *_age; // 年龄 } - (instancetype)initWithName:(NSString *)name gender:(NSString *)gender age:(NSString *)age; @end
子类定义如下:
@interface Student : Person { NSString *_number; // 学号, 子类特有的实例变量 } - (instancetype)initWithName:(NSString *)name gender:(NSString *)gender age:(NSString *)age number:(NSString *)number; @end
实现子类初始化方法:
- (instancetype)initWithName:(NSString *)name gender:(NSString *)gender age:(NSString *)age number:(NSString *)number { self = [super initWithName:name gender:gender age:age]; if (self) { _number = number; } return self; }
同理:
父类Person作为NSObject类的子类
它的初始化方法实现如下:- (instancetype)initWithName:(NSString *)name gender:(NSString *)gender age:(NSString *)age { self = [super init]; if (self) { _name = name; _gender = gender; _age = age; } return self; }
便利构造器
利用类方法初始化一个对象
如上例中的Student类
再声明并实现一个方法如下:+ (instancetype)studentWithName:(NSString *)name gender:(NSString *)gender age:(NSString *)age number:(NSString *)number; + (instancetype)studentWithName:(NSString *)name gender:(NSString *)gender age:(NSString *)age number:(NSString *)number { Student *student = [[Student alloc] initWithName:name gender:gender age:age number:number]; return student; }
这样就实现了在类中把alloc 和init 方法封装在类中的方法
便利构造器的优点:
- 减化代码
便利构造器和自定义初始化方法完成的功能是一样的 只是在调用的时候格式不同
但是通常两种初始化方法都要有, 这是为了在工作合作中便于他人的习惯