Objective-C学习篇03—继承
大纲:
继承的基本概念
自定义初始化方法
便利构造器方法
重写description方法
一 继承基本概念
程序里的对象和"人类"的对象是一样的,高富帅继承了父母,自然就拥有了父母所有的资源,子类继承了父类同样就拥有了父类所有的属性和方法,当然,父类私有的除外.
我们在定义一个新的类的时候,常常会遇到要定义的新类是某个类的扩展或者是某个类的修正等这种情况.如果能够在已有的类的基础上定义新类,那么新类的定义将会变得十分简便.
类似于这种,通过扩展或者修改既有的类来定义新类的方法就叫做继承(inheritance).在继承关系中,被继承的类(原有的类)就成为父类(superclass),通过这种继承关系得到的新类就叫做子类(subclass).
上面也讲到,继承意味着子类可以得到父类所有的属性和方法,除此之外,子类还可以为新类:
1. 添加新的实例变量
2. 追加新的自己特有的方法
3. 重新定义父类中的方法
当然,如果子类中只追加新的实例变量而不变更方法则没有任何意义.可以说,继承的目的就是要让子类能做更多父类做不了的事(扩展方法).子类中重新定义父类的方法叫做重写.分为完全重写和不完全重写,这些在后面都会讲到.
继承的优点:1> 省略了大量重复的代码 2>建立起类与类之间的关系.
缺点在于类间耦合性太强
二 OC中的继承关系
1. 在OC中,一旦建立了继承关系,子类就可以使用父类中所有的实例变量和方法.(父类私有的除外)
2. OC中的继承是单继承关系,一个类只能有一个父类,但是一个类可以有n个子类.
3. OC中的继承关系一定是合理的
4. OC中继承是单根类继承,所有类的祖宗类都是NSObject,NSObject是所有类的基类或者根类
5. A 继承自 B 可以说,A是B的子类,或者说A类是由B类衍生的,衍生类
6. 使用继承的好处:如果一些类在定义的时候,发现他们有很多的实例变量或者方法是重复的(共同的特征),此时就可以把这些实例变量或者方法抽象出一个新的类作为这些类的父类,这样我们在定义一个类的时候,直接将要的定义的类继承自这个父类,就能省去很多重复的代码,只需要在子类中添加自己独有的特征和方法.
7. 父类中被@public和@protected修饰的实例变量,子类中可以直接访问,而被@provate(私有的)修饰的实例变量子类不能直接访问,只能通过方法访问
8. 子类在调用方法时优先去自己的类里面找,找到了就直接调用,找不到就去父类中找,找到就调用,找不到就直接向上找,直到最终找到NSObject,如果实现就调用,没有实现就Crash
9. 重写父类的方法,一种是完全重写,不完全重写时需要在方法中使用super调用父类对方法的实现
使用继承时要注意的问题
- OC中不允许子类和父类相同名称的实例变量,会引起混乱;
- OC中子类可以拥有和父类相同名称的方法,在子类调用时,优先去自己的内部找,找到就调用,没有就一层一层往上找.
- 子类的重写会覆盖掉父类以前的实现,因为子类重新实现了父类中的某个方法
代码演示:
首先,创建一个汽车Car类,让它继承自NSObject类
Car.h
@interface Car : NSObject // 汽车的特征 { @public NSString *_color; //颜色 @protected NSString *_brand; //品牌 @private NSInteger _wheel; //轮子 } // 车的行为 - (void)run; - (void)carShock; // 实例变量的setter getter方法 - (void)setColor:(NSString *)color; - (NSString *)color; - (void)setBrand:(NSString *)brand; - (NSString *)brand; - (void)setWheel:(NSInteger)wheel; - (NSInteger)wheel; @end
Car.m
@implementation Car - (void)run { NSLog(@"车在跑"); } - (void)carShock { NSLog(@"车震是什么感觉,没试过"); } - (void)setColor:(NSString *)color { _color = color; } - (NSString *)color { return _color; } - (void)setBrand:(NSString *)brand { _brand = brand; } - (NSString *)brand { return _brand; } - (void)setWheel:(NSInteger)wheel { _wheel = wheel; } - (NSInteger)wheel { return _wheel; } @end
上面我们定义了一个汽车Car的类,,使它具有颜色,品牌,轮子的属性,具有跑和车震两种行为(方法).现在,我们给卡车Truck增加一个最大载货量的属性和输出自身信息的方法,同时又包含Car这些属性和方法,那么我们就要使用继承;
这个时候利用继承的思想,我们只需要新建一个类,让它继承自Car这个类,新类中写自己特有的方法和属性.
新建一个Truck类,继承自Car:
Truck.h
@interface Truck : Car { // NSString *_color; error 子类中不能定义和父类同名的实例变量 CGFloat _maxLoad; //最大载货量 } - (void)setMaxLoad:(CGFloat)manLoad; - (CGFloat)maxLoad; // 描述卡车信息的方法 - (void)describeTruck; - (void)run; // 子类中可以定义和父类同名的方法,这种形式叫做方法的重写//那么,为什么要进行方法的重写呢?因为父类提供的方法不能满足子类的需求// 重写父类的方法有两种,一种是完全重写父类的方法,另一种是不完全重写的方法 @end
Truck.m
@implementation Truck // _maxLoad的setter getter方法 - (void)setMaxLoad:(CGFloat)manLoad { _maxLoad = manLoad; } - (CGFloat)maxLoad { return _maxLoad; } // 描述卡车信息的方法 - (void)describeTruck { NSLog(@"%@ %@ %ld %.2f", _color, _brand, self.wheel, _maxLoad); } // 注意:由于 _wheel被private修饰,不能在子类中直接访问,需要用self // 重写父类run的方法 // 如果在重写父类方法时,没有调用父类的方法实现就叫做完全重写 - (void)run NSLog(@"卡车在奔跑"); } // 重写车震的方法 - (void)carShock { // super 编译器指令,用于调用父类的方法,适用于想保留父类中某些方法的情况 [super carShock]; NSLog(@"卡车不好震"); } @end
回到main中.
// 创建一个大卡车的对象 Truck *truc = [[Truck alloc] init]; // color brand wheel是从父类继承过来的 truc.color = @"蓝色"; // 颜色 truc.brand = @"东风"; // 品牌 truc.wheel = ; // 轮子 // maxLoad是子类特有的 truc.maxLoad = 120.0;// 载重量 // 调用父类的方法 // [truc carShock]; [truc run]; // 调用描述卡车的方法 [truc describeTruck]; // 调用车震的方法 [truc carShock];
打印结果:
2015-11-26 22:46:44.741 OCLessonInherit-03[1759:364404] 卡车在奔跑
2015-11-26 22:46:44.742 OCLessonInherit-03[1759:364404] 蓝色东风 12 120.00
2015-11-26 22:46:44.742 OCLessonInherit-03[1759:364404] 没震过,不知道什么感觉
2015-11-26 22:46:44.742 OCLessonInherit-03[1759:364404] 卡车不好震
三 初始化方法
- 完整的初始化方法
- (id)init { // 执行父类中的init方法 self = [super init] if (self) { // 初始化设置 _taxiMeter = @"计价器"; } // 返回初始化完成的对象 return self; }
初始化过程:
初始化时,先调用父类的[super init]方法(向上递归到NSObject类中的初始化方法),使用self接收结果,然后根据self中的值判断是否为nil,决定要不要初始自己的实例变量;如果为nil,说明父类的初始化方法失败了,就不再对自身实例变量初始化,如果不为nil,说明父类初始化方法成功了,再对自身实例变量进行初始化
2.自定义初始化方法
命名规范:
1. 一定是对象方法,以减号 - 开头
2. 返回值类型是 id 类型(最好写id类型) 或者是当前的类类型
3. 必须以 initWith 开头
4. 自定义初始化方法分为两种:完全自定义初始化 与 不完全初始化方法
示例:
不完全初始化方法:
// 只初始化姓名和年龄 - (id)initWithName:(NSString *)name age:(NSInteger)age { if (self = [super init]) { _name = name; _age = age; } return self; }
完全自定义初始化:
// 初始化全部的实例变量
- (id)initWithName:(NSString *)name age:(NSInteger)age height:(CGFloat)height { if (self = [super init]) { _name = name; _age = age; _height = height; } return self; }
3.便利构造器方法
命名规范
1. 遍历构造器方法一定是一个类方法,以加号开头(+)
2. 返回值类型一定是 id类型
3. 以类名小写开头 +With+实例变量名去掉下划线且首字母大写
注意:
4. 便利构造器又叫做工厂方法,用于快捷快速地创建对象
5. 便利构造器方法的实质: 在方法内部把一个创建好的对象作为方法的返回值
6. 便利构造器方法要和自定义初始化方法配套使用
示例(对应上面列举的两种自定义初始化方法):
+ (id)personWithName:(NSString *)name age:(NSInteger)age { // 将一个创建好的对象作为便利构造器方法的返回值 return [[Person alloc] initWithName:name age:age]; } + (id)personWithName:(NSString *)name age:(NSInteger)age hight:(CGFloat)hight { return [[Person alloc] initWithName:name age:age height:hight]; } @end
main
Person *p = [[Person alloc] init]; NSLog(@"%@ - %ld - %.2f", p.name, p.age, p.height); //调用自定义初始化方法 Person *p1 = [[Person alloc] initWithName:@"吴邪"]; NSLog(@"%@ - %ld - %.2f", p1.name, p1.age, p1.height); Person *p2 = [[Person alloc] initWithName:]; NSLog(@"%@ - %ld - %.2f", p2.name, p2.age, p2.height); // 调用完全初始化方法 Person *p3 = [[Person alloc] initWithName: height:]; NSLog(@"%@ - %ld - %.2f", p3.name, p3.age, p3.height); // 调用便利构造器方法 Person *p4 = [Person personWithName:@"杀阡陌"]; NSLog(@"%@ - %ld - %.2f", p4.name, p4.age, p4.height); Person *p5 = [Person personWithName:]; p5.name = @"糖宝"; p5.height = 0.3; NSLog(@"%@ - %ld - %.2f", p5.name, p5.age, p5.height); Person *p6 = [Person personWithName: hight:1.65]; NSLog(@"%@ - %ld - %.2f", p6.name, p6.age, p6.height);
以下是完整的代码:
Person.h
#import "Car.h" @interface Person : Car { NSString *_name; NSInteger _age; CGFloat _height; } - (void)setName:(NSString *)name; - (NSString *)name; - (void)setAge:(NSInteger)age; - (NSInteger)age; - (void)setHeight:(CGFloat)height; - (CGFloat)height; // 自定义初始化方法 - (id)initWithName:(NSString *)name; - (id)initWithName:(NSString *)name age:(NSInteger)age; - (id)initWithName:(NSString *)name age:(NSInteger)age height:(CGFloat)height; // 写一个遍历构造器的类方法 + (id)personWithName:(NSString *)name; + (id)personWithName:(NSString *)name age:(NSInteger)age; + (id)personWithName:(NSString *)name age:(NSInteger)age hight:(CGFloat)hight; @end
#import "Person.h" @implementation Person - (void)setName:(NSString *)name { _name = name; } - (NSString *)name { return _name; } - (void)setAge:(NSInteger)age { _age = age; } - (NSInteger)age { return _age; } - (void)setHeight:(CGFloat)height { _height = height; } - (CGFloat)height { return _height; } // 自定义初始化方法 // 无论是哪种初始化方法,都要首先判断下父类有没有有初始化成功 - (id)initWithName:(NSString *)name { if (self = [super init]) { _name = name; } return self; } - (id)initWithName:(NSString *)name age:(NSInteger)age { if (self = [super init]) { _name = name; _age = age; } return self; } - (id)initWithName:(NSString *)name age:(NSInteger)age height:(CGFloat)height { if (self = [super init]) { _name = name; _age = age; _height = height; } return self; } + (id)personWithName:(NSString *)name { // 便利构造器的实质就是在便利构造器方法中创建一个对象,然后把这个对象作为返回值返回 // 遍历构造器方法一定要和自定义初始化方法配合使用 Person *p = [[Person alloc] initWithName:name]; return p; } + (id)personWithName:(NSString *)name age:(NSInteger)age { // 将一个创建好的对象作为便利构造器方法的返回值 return [[Person alloc] initWithName:name age:age]; } + (id)personWithName:(NSString *)name age:(NSInteger)age hight:(CGFloat)hight { return [[Person alloc] initWithName:name age:age height:hight]; } @end
Objective-C学习篇03—继承的更多相关文章
- 深入js的面向对象学习篇(继承篇)——温故知新(三)
写这篇有关继承的文章时,突然想起,几天前的面试.因为习惯在学习知识的时候加上自己的理解,很喜欢用自己话来解释,于是乎当面试被问起继承原理时,噼里啪啦一大堆都是自己组织的话,(也可能是因为个人紧张.外加 ...
- 【从零开始自制CPU之学习篇03】锁存器与触发器
本篇学习了两种锁存器:SR Latch和D Latch,一种触发器:D flip flop SR Latch:SR—锁存器 初始状态下,S和R都为0,Q和Q‘随机有一个为1另一个 为0(取决于电流速度 ...
- pythonchallenge之C++学习篇-03
提示说一个小写字母两面精确地被大写字母包围,应该指的是周围没有四个而仅仅这两个像这样的:xXXXxXXXx的中间的那个应该是符合条件的 好了标题是re,提示该是使用正则表达式,网页源码里有待处理的字符 ...
- c++学习笔记之继承篇
title: c++学习笔记之继承篇 date: 2017-03-26 16:36:33 tags: [c++,继承,public,virtual,private,protected] categor ...
- javascript面向对象系列第三篇——实现继承的3种形式
× 目录 [1]原型继承 [2]伪类继承 [3]组合继承 前面的话 学习如何创建对象是理解面向对象编程的第一步,第二步是理解继承.本文是javascript面向对象系列第三篇——实现继承的3种形式 [ ...
- (转载)OC学习篇之---概述
前言 终于开启了OC的学习篇了,之前由于工作上的事,学习就一直搁浅了,不过最近由于各种原因,感觉必须要开启iOS的开发旅程了,不然就老了.因为之前一直是做Android的,所以学习iOS来就没那么费劲 ...
- Scrapy学习篇(十)之下载器中间件(Downloader Middleware)
下载器中间件是介于Scrapy的request/response处理的钩子框架,是用于全局修改Scrapy request和response的一个轻量.底层的系统. 激活Downloader Midd ...
- Scrapy学习篇(七)之Item Pipeline
在之前的Scrapy学习篇(四)之数据的存储的章节中,我们其实已经使用了Item Pipeline,那一章节主要的目的是形成一个笼统的认识,知道scrapy能干些什么,但是,为了形成一个更加全面的体系 ...
- OpenCV 学习笔记03 findContours函数
opencv-python 4.0.1 1 函数释义 词义:发现轮廓! 从二进制图像中查找轮廓(Finds contours in a binary image):轮廓是形状分析和物体检测和识别的 ...
随机推荐
- cf C Milking cows
题意:输入n,然后输入n个数,在n个数中0或1,0代表这头牛向左看,1代表这头牛向右看,问最后最少损失多少牛奶. 思路:贪心,连着的0可以不损失,一旦插入1就会损失牛奶. #include <c ...
- MFC任务管理器task manager----进程的挂起与恢复--NtSuspendProcess&&NtResumeProcess
http://hi.baidu.com/xbbsh/blog/item/b73d3125462201084c088db1.html ---------------------------------- ...
- 用c写99乘法表
int main(int argc,char **argv){ int a; for(a=1;a<=9;a++){ int b; for(b=1;b<=a;b++){ printf(&qu ...
- 图论(Tarjan缩点):BZOJ 1194: [HNOI2006]潘多拉的盒子
1194: [HNOI2006]潘多拉的盒子 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 344 Solved: 181[Submit][Stat ...
- C语言数据类型的理解
数据类型的定义: 作为一种语言,必然有所谓的语言组成要素,就像日常生活中人们之间的交流一样,首先会有字,字再成词组,再来就是句子,后来呢就是段落等等.当然不同的字,词,句这些在一起,就会有不同的表达效 ...
- ACM第六周竞赛题目——A LightOJ 1317
A - A Time Limit:2000MS Memory Limit:32768KB 64bit IO Format:%lld & %llu Submit Status P ...
- TCP内核源码分析笔记
Table of Contents 1 术语 1.1 ABC 1.2 SACK 1.3 D-SACK 1.4 FACK 1.5 F-RTO 1.6 nagle算法 1.7 cork算法 1.8 tem ...
- RabbitMQ-死信(Dead Letter)
对于有异常的消息我们可以有如下做法: 记录下来再ack. nack或者reject,同时将requeue设为false. 在第2条的基础上增加死信(Dead Letter). 上边的第3个做法可以 ...
- springboot 配置文件 .properties和.yml的写法区别
例如 : redis配置的properties或yml文件,如下: spring.redis.cluster.nodes[]= spring.redis.cluster.nodes[]= 或 s ...
- BDD框架之lettuce---python3.+安装报错
跟虫师学习python,学到BDD框架之lettuce( http://www.cnblogs.com/fnng/p/3415609.html),发现python3.5环境下安装lettuce后无法正 ...