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):轮廓是形状分析和物体检测和识别的 ...
随机推荐
- The Suspects(简单的并查集)
Description Severe acute respiratory syndrome (SARS), an atypical pneumonia of unknown aetiology, wa ...
- Python4Delphi也是与VCL密切相关,所以才能相互调用,绝对有研究价值!
Python4Delphi也是与VCL密切相关,所以才能相互调用,绝对有研究价值! http://www.cnblogs.com/GarfieldTom/archive/2013/01/17/2864 ...
- Lua table使用
days = {"Sunday", "Monday", "Tuesday", "Wednesday", "Th ...
- Unity NGUI实现技能CD效果
unity版本:4.5.1 NGUI版本:3.6.5 脚本代码:C# 在游戏中经常要实现技能的CD效果,NGUI中已经实现了这个功能,即在button上创建一个半透明的Sprite实现这个功能. 首先 ...
- 通过redis-rdb-tools分析redis内存使用量
背景:生产上一台redis服务器,物理内存32G,几个项目都在用,经常不足一月内存就会耗尽,然后开始使用swap,当swap也用尽的时候,系统就宕机.redis配置也优化过几次,但未见成效.因此决定看 ...
- 数学(欧拉函数):UVAOJ 11426 GCD - Extreme (II)
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAB4AAAAQ4CAIAAABnsVYUAAAgAElEQVR4nOzdPW7zvII/bG1gCi9gKq ...
- Android 开机自启动
首先实现开机自启动: 第一步创建一个广播接收者,如MyBootBroadcastReceiver.java package com.example; import android.content.Br ...
- HDOJ(HDU) 2153 仙人球的残影(谜一样的题、、、)
Problem Description 在美丽的HDU,有一名大三的同学,他的速度是众所周知的,跑100米仅仅用了2秒47,在他跑步过程中会留下残影的哎,大家很想知道他是谁了吧,他叫仙人球,既然名字这 ...
- poj1038
题目大意:网络导航? 标准的web浏览器包含向前和向后浏览最近的页面的特性,有一个方法来实现这些用两个栈来跟踪页面达到向前和向后的移动,在这个问题里面,你被要求实现这些. 以下命令需要支持: BACK ...
- WCF:如何将net.tcp协议寄宿到IIS
1 部署IIS 1.1 安装WAS IIS原本是不支持非HTTP协议的服务,为了让IIS支持net.tcp,必须先安装WAS(Windows Process Activation Service),即 ...