【Objective-C】4 继承与多态
第四节 继承与多态
01 Xcode文档的安装
-
apple 提供了很多框架,框架中有很多类 / 函数 / 数据类型
---> 问题:
- 如何知道有哪些框架
- 框架中有哪些类
- 类中有什么方法
- 如何调用
---> 答案均在 Xcode 文档中
Xcode 文档:window --> developer documentation
02 static 关键字
-
C 语言中的 static
修饰局部变量 / 全局变量 / 函数
在 OC 中,static 不能修饰属性,也不能修饰方法,但可以修饰方法中的局部变量
---> 若 static 修饰了方法中的局部变量,则该变量变为静态变量,存储在常量区,在方法执行完毕后不会被回收,下次执行方法时不再重新创建,而是直接使用
举例:学生编号自动 + 1,不由用户输入
--> 令编号为创建对象的类方法中的一个静态变量,每调用一次该变量的值 + 1
-
注意:当类方法返回值为类的对象时,返回值类型建议不要写类名,写 instancetype
@interface Person : NSObject { NSString *_name; } + (instancetype)person; @end
03 self 关键字
在方法内部可以定义一个与属性名相同的局部变量。
此时在方法中该名称指代方法的局部变量,无法访问对象的属性。
3.1 self 使用情景
self 是一个指针,可以在对象方法(指向当前对象) & 类方法(指向当前类 --> 存储代码段中类加载后的地址)中使用
-
方法内部存在与属性名相同的局部变量,若想访问同名属性,使用 self->_xxx
规避该情景的方法:属性名以 _ 下划线开头,方法中的局部变量名直接写名词
-
在方法内想要调用另一个方法,使用 [self xxx]
3.2 self 在类方法中的使用
可以在本类中调用其他类方法(其实可以使用类名调用,但建议使用self)
-
取到类在代码段中的地址的方法
-
通过调试查看对象中 isa 指针的值(isa 指针禁止访问,只能查看,无法实际“取到”)
-
在类方法中查看 self 的值
+ (void)test{ NSLog(@"self = %p", self); } -
调用对象的对象方法 class ,可以返回类的地址
Person *p = [Person new]; NSLog(@"%p", [p class]); // 输出 p 所属的类的地址 -
调用类的类方法 class,也可以返回地址
NSLog(@"%p", [Person class]); // 输出 Person 类的地址注意:对象方法 & 类方法可以重名(调用方式不同),但对象方法之间 & 类方法之间不可重名
-
04 继承
---> 当多个类具有相同的成员 --> 大量复制粘贴,造成代码冗余,后期维护难
现实中的继承:子代无条件拥有父代的资产,非子代不能拥有;父代的资产由父代创建
代码中的继承:子类拥有父类中所有的成员,想直接继承,不必重新定义
4.1 继承语法
// Person 类已创建
// 想让Chinese 类继承 Person 的全部成员(“人”的属性 & 行为“中国人”都有)
@interface Chinese : Person
// Chinese 类从 Person 类 继承 / 派生
// Chinese 是 Person 的子类 / 派生类,Person 是 Chinese 的父类 / 基类
@end
4.2 使用注意
-
在新创建时可以在弹窗中的 subclass of 中指定父类,Xcode 自动生成相应代码
-
继承是类继承,而不是对象继承,对象与对象之间毫无关系
-
继承是有条件的,乱继承往往不符合逻辑要求
---> 判断是否满足继承关系 :... is a ...
A is a B --> A 可以继承 B (学生是人,车是交通工具,...)
-
如果有一个成员并不是所有的子类都有,那么就不应该定义在父类中
例:学生有学号,但老师没有 ---> 学号属性不应定义在 Person 类中;
交通工具不是都能在地面行驶(船,飞机。。)---> 在地面上行驶的方法不应定义在父类中
-
子类中不可存在和父类同名的属性 & 方法
4.3 继承的特点
- 单根性:一个类只能有一个父类
- 传递性:A从B继承,B从C继承 ---> A同时拥有B C 的成员
4.4 NSObject 类
是 Foundation 框架中的类,其中包含类方法 new。
该方法用于创建一个新对象,返回值为这个对象的指针
---> 所有类必须直接或间接的从 NSObject 继承,否则将无法创建对象
NSObject 类中还包含属性 isa,因此所有类的对象都包含一个 isa 指针
4.5 super 关键字
-
在对象方法中使用
[super xxxx]:用于调用当前对象从父类继承来的对象方法
---> 其实使用 [self xxxx] 也可以实现,因为子类继承了父类的全部
-
在类方法中使用
[super xxxx]:用于调用当前类从父类继承来的类方法
---> 其实使用 [self xxxx] 或 [子类名 xxxx] 或 [父类名 xxxx] 也可以实现
---> 还是建议使用 super,增强可读性
- 注意:super 只能用来调用方法,不可访问属性
05 私有属性与私有方法
5.1 访问修饰符
用来修饰属性,限定了属性的使用范围
@private:私有的,使属性只能在本类的内部访问
---> 只有本类的方法实现可以访问,其他类(包括子类) & 外部函数均不可访问)
@protected:受保护的,使属性只能在本类和其子类的内部访问
@package:使属性可以在当前框架中使用(需要自己写框架时才会用到)
@public:公共的,可以任意访问
-
注意:
-
如果属性未被指定访问修饰符,系统默认为 @protected
-
子类可以继承父类的私有属性,但子类内部无法直接访问 --> 可以通过父类的 setter & getter 方法访问
-
作用范围:从当前访问修饰符开始,到另一个访问修饰符 或 } 大括号结束
-
使用建议:
@public:不要使用(不要让属性暴露给外部)
@private:只在本类中使用 ---> 不需要写 getter & setter,如果需要写就不要用(没必要)
@protected:一般情况下,都推荐使用默认的 @protected
-
只能修饰属性,不可修饰方法
-
5.2 私有属性
-
@private 修饰的属性称为私有属性,只能在类的内部访问。
但在外部,Xcode 仍会提示这个对象中有这个属性,只是无权访问如何“真私有”?让外界不知道内部存在这个属性?
---> 将属性定义在 @implementation 的大括号中,此时属性为私有属性,各种访问修饰符均无效,外界不会提示更无法访问
@implementation Person { @public int _age; // 即使前面缀有 @public 访问修饰符,这个属性始终私有 } @end
这两种定义属性的方式都会产生私有属性,唯一的区别在于 Xcode 能否产生提示
5.3 私有方法
方法不想被外界调用,只在内部使用?
---> 只写实现,不写声明 ---> 只能在本类的其他方法内使用
06 多态
6.1 里氏替换原则(LSP)
子类可以替换父类的位置,且程序的功能不受影响。
表现形式:让一个父类指针指针子类对象
Person *p = [Student new]; // 合法
Student *s = [Student new];
// Person 类中的类方法: + (void)person:(Person *)p1 TalkToPerson(Person *)p2;
[Person person:p TalkToPerson:s]; // 合法
为什么?
- 逻辑上:学生也是人,请求要一个人,当然可以给一个学生
- 代码上:父类拥有的成员子类中都有,不会影响程序的功能
作用:指针不仅可以当前类的对象在堆区的地址,也可以存储其子类的对象的地址
- 如果一个指针类型为 NSObject,那么这个指针可以指向任意的 OC 对象
- 如果一个数组的元素类型为 OC类的指针,那么该数组的元素可以存储这个 OC 类及其任意子类的对象的地址
---> 因此有:
NSObject *ob[3];
ob[0] = [Person new];
ob[1] = [Student new];
ob[2] = @"jack"; // NSString 也是一个 OC 类
// 总结:NSObject 指针类型数组可以存储任意值
注意:当父类指针指向子类的对象时,该指针只能访问子类对象中的父类成员
6.2 方法重写
子类继承了父类的方法 --> 子类通过继承,拥有了相同的功能
问题:子类实现该功能的方式可能和父类不同
---> 在子类中重写方法:在 @implementation 中写出该方法在当前类中的实现
注意:当父类指针指向子类的对象时,若方法在子类中重写了,那么调用的就是重写的方法
6.3 多态
表示同一个行为,在不同事物上具有不同的表现形式。
举例:【 cut】
医生 - 切除 / 理发师 - 理发 / 演员 - 结束表演 ...
多态的优点:方便后期维护修改
6.4 description 方法
-
%p:打印指针变量的值 --> 存储的地址
%@:打印指针变量指向的对象
---> 如果使用 %@ 打印一个对象,输出的格式为 <对象名所属的类名:对象的地址>
原理:打印对象时,NSLog 函数的底层实现
- 调用传入的对象的 description 方法
- 拿到方法的返回值,返回值为一个字符串
- 将这个字符串输出
description 方法是定义在 NSObject 类中的对象方法 --> 每一个 OC 对象都有这个方法
该方法返回的字符串格式为 @"<对象所属的类名:对象的地址>"
想换一种方式打印对象?---> 在类中重写 description 方法。通过自定义返回的字符串格式来改变打印结果
// 声明省略
@implementation Person
- (NSString *)description{
return [NSString stringWithFormat:@"姓名:%@,年龄:%d", _name, _age];
}
@end
int main(){
Person *p = [Person new];
[p setName:@"jack"];
[p setAge:18];
NSLog(@"%@", p); // 输出【姓名:jack,年龄:18】
}

浙公网安备 33010602011771号