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调用父类对方法的实现

  使用继承时要注意的问题

  1. OC中不允许子类和父类相同名称的实例变量,会引起混乱;
  2. OC中子类可以拥有和父类相同名称的方法,在子类调用时,优先去自己的内部找,找到就调用,没有就一层一层往上找.
  3. 子类的重写会覆盖掉父类以前的实现,因为子类重新实现了父类中的某个方法

  代码演示:
  首先,创建一个汽车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 = 12;     // 轮子

// 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] 卡车不好震

三 初始化方法

  1. 完整的初始化方法
-  (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:@"花千骨" age:18];
    NSLog(@"%@ - %ld - %.2f", p2.name, p2.age, p2.height);     // 调用完全初始化方法
    Person *p3 = [[Person alloc] initWithName:@"白子画" age:22 height:70];
    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:@"东方彧卿" age:15];
    p5.name = @"糖宝";
    p5.height = 0.3;
    NSLog(@"%@ - %ld - %.2f", p5.name, p5.age, p5.height);

    Person *p6 = [Person personWithName:@"单春秋" age:26 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
时间: 2025-01-01 11:47:59

Objective-C学习篇03—继承的相关文章

【从零开始自制CPU之学习篇03】锁存器与触发器

本篇学习了两种锁存器:SR Latch和D Latch,一种触发器:D flip flop SR Latch:SR—锁存器 初始状态下,S和R都为0,Q和Q‘随机有一个为1另一个 为0(取决于电流速度).当把S弄成1后,Q为1,此后无论S怎么变化Q都为1.对R也是如此. 一句话总结就是:输出端可以记住S和R最后一次为1的是哪个.这个记住就是存了. D Latch:D—锁存器 此为对SR锁存器的优化,D相当于原来的S和R,这不但方便操作,还屏蔽了S和R均为1这种不规范操作. EN是enable的意

pythonchallenge之C++学习篇-03

提示说一个小写字母两面精确地被大写字母包围,应该指的是周围没有四个而仅仅这两个像这样的:xXXXxXXXx的中间的那个应该是符合条件的 好了标题是re,提示该是使用正则表达式,网页源码里有待处理的字符串,所以这里应该是主要考察的正则表达式 C++里的正则表达式有一些类库类实现,有多个实现,他们是什么以及比较可以参考这里 这里打算采用比较出名的boost,boost好像不止包含正则表达式的功能,具体介绍参照维基 直接包含它显示说没有这个文件,那么它还是需要安装的,我猜安装它可能也需要一些曲折的路线

html5学习篇:03.字体样式相关

1.粗体 b 2.斜体 i 3.等宽字体tt 4.小一号字体:small 5.删除线 del 6.下划线:ins 7.上标下标 sup sub 8.高亮 mark <!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>段落相关</title> </head> <body> <h

OC学习篇之---总结和学习目录

今天终于把OC的基础知识学习完了,但是这些知识只是最基础的,还有很多高级知识,这个可能需要后面慢慢的去学习才能体会到.下面就是这次学习OC的目录教程,如果大家发现有什么不正确的地方,请指正,小弟是新生,多请OC老鸟来喷~~ 1.OC学习篇之---概述 2.OC学习篇之---第一个程序HelloWorld 3.OC学习篇之---类的定义 4.OC学习篇之---类的初始化方法和点语法的使用 5.OC学习篇之---类的三大特性(封装,继承,多态) 6.OC学习篇之[email protected]关键

JAVA学习篇--Java类加载

由来: 与普通程序不同的是,Java程序(class文件)并不是本地的可执行程序(解释性语言).当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Javaclass加载到JVM里头运行,负责加载Javaclass的这部分就ClassLoader.中文叫做类加载器. 类加载器就好比一个代理,你需要什么,我通过类加载器将你需要的内容返回给你! 类加载器有什么作用? 当程序需要的某个类,那么需要通过类加载器把类的二进制加载到内存中. 解释: 类加载器也是Java类,因为其他是java类的

(转载)OC学习篇之---概述

前言 终于开启了OC的学习篇了,之前由于工作上的事,学习就一直搁浅了,不过最近由于各种原因,感觉必须要开启iOS的开发旅程了,不然就老了.因为之前一直是做Android的,所以学习iOS来就没那么费劲了,当然我们知道,Android是Java语言支撑的,iOS是OC支撑的,关于OC的学习,会和Java相对比这来,这样效率也会高点,同时在大学里学了C/C++所以,学习OC就没什么难度了,就是一套新的api. 概述 目前来说,Objective-C(简称OC)是iOS开发的核心语言,在开发过程中也会

cocos2d-x学习篇之计时器

Cocos2d-x学习篇之简单的时钟 由于C++很菜,在做这个小工具时遇到各种各样蛋疼的问题,甚至准备放弃了,不过,最后还是搞出来了,真尼玛不容易啊. 刚开始想,显示本地时间不是很容易吗,取计算机的当前时间拼接成字符串显示在CCLabelTTF的实例上不就行了吗,几分钟的事,结果整整搞了一天. 首先,cocos2d-x不支持汉字显示,也就是不支持Unicode编码,从网上找了方法来转换一下,如下: #ifndef CHINESE_H #define CHINESE_H #define _GLIB

iOS开发项目篇—03添加导航控制器

iOS开发项目篇—03添加导航控制器 一.简单说明 分析:分析微博应用,我们需要给每个子控制器都添加一个导航控制器(每个子控制器的导航不一样),所以需要新建一个导航控制器,然后把该导航控制器作为window的根控制器,添加的四个子控制器,分别添加在导航控制器上,也就是说整个项目采用当前主流的UI框架,一个UITabBarController管理着四个UINavigationController,而每个UINavigationController则分别管理着“首页”.“消息”.“发现”和“我”这四

J2EE学习篇之--Struts2技术详解

前面说到了Struts1的相关知识,下面来说一下Struts2的相关知识,我们知道现在Struts2使用的比Struts1多,Struts2已经替代Struts1成为主流的框架了... 摘要 Struts2是在WebWork2基础发展而来的.和struts1一样, Struts2也属于MVC框架.不过有一点大家需要注意的是:尽管Struts2和Struts1在名字上的差别不是很大,但Struts2和struts1在代码编写风格上几乎是不一样的.那么既然有了struts1,为何还要推出struts