如果你还不理解多态,不妨看看我的

多态(Objective-C)

一、多态的概念

场景1:

老师:【操着四川口音】,童鞋门,今天,老师给你们讲讲神马是多态。
小明:啥?堕胎?【淫笑】。
老师:小明,你出去。

多态:在程序员的世界,多态就是相同的方法,不同的实现。

场景2:

老师:请同学们用“欣欣向荣”来造句。
小红:祖国的事业欣欣向荣。
小明:欣欣向荣荣表白,遭到拒绝。
老师:小明,你出去。

如果说用欣欣向荣造句是一条消息,小红和小明是两个对象,我们可以看到,不同的对象,响应相同的消息,得到的是完全不同的结果。这种不同对象响应相同消息的能力就是【多态】

把上面的场景翻译成程序员语

定义一个Boy类

@interface Boy : NSObject

- (void)makeStatementsWithWord:(NSString*)word;


@end
@implementation Boy

- (void)makeStatementsWithWord:(NSString*)word {
    NSLog(@"%@荣表白,遭到了拒绝",word);
}
@end

定义一个Girl类

@interface Girl : NSObject

- (void)makeStatementsWithWord:(NSString*)word;

@end
@implementation Girl

- (void)makeStatementsWithWord:(NSString*)word {
    NSLog(@"祖国的建设事业 %@",word);
}

@end

两个类都带一个方法名为- (void)makeStatementsWithWord:(NSString*)word;的实例方法。这个方法的参数表示用来造句的词。

main函数如下

    Boy* xiaoMing = [[Boy alloc] init];
    [xiaoMing makeStatementsWithWord:@"欣欣向荣"];

    Girl* xiaoHong = [[Girl alloc] init];
    [xiaoHong makeStatementsWithWord:@"欣欣向荣"];

打印结果如下:

20XX-XX-XX 11:04:31.990 test[2751:393671] 欣欣向荣荣表白,遭到了拒绝
20XX-XX-XX 11:04:31.991 test[2751:393671] 祖国的建设事业 欣欣向荣

如果你已经能举出一些类似的例子,那说明你已经对多态有一个初步的认识了。

下面我要讲一个故事。通过这个故事,你会加深对多态的理解。

二、动态绑定和id类型

大欢和大鹏是我的两位得力助手,大欢是讲师,人称刀疤欢,他的“刀疤”是被树枝刮伤的。大鹏是程序员,人称傻鹏,显然我们不必再去讨论他的智商问题。

故事就发生在他们俩身上。
现在我需要他们俩去帮我给一位新来的女同事做一个面试,然后给我一个反馈结果。我给这个帮我做面试的人起了一个代号,叫simple(一个头脑简单的面试官)。显然我公司对待面试是很重视的。

然后,我先让大欢上场,所以这个时候simple就是大欢。
然后大欢经过2个小时的面试,给出我一个这样简单的反馈:

1.沟通能力很强
2.编码能力很强
3.项目经验丰富
4.薪资要求很低
非常适合我们公司

显然对于我这样一个严格要求能力的人,这样的答复是不够的。我还批评了大欢做事太粗糙。

然后我决定让大鹏再去帮我做一个面试。这个时候simple就是大鹏。大鹏经过5分钟以后也回来给了我一个反馈。

1.很漂亮
2.没有男朋友

最后经过慎重的考虑,我决定录用她了。

【拍砖】哎哎,课还没讲完呢你们都干嘛去啊?

好我们正经一点,如果simple能表示大欢又能表示大鹏,那么它到底是个什么类型呢?是讲师类型还是程序员类型? 显然用哪一种都不合适。所以,OC给我们提供一种更加宽泛的类型,即id类型。
id类型可以表示任意的对象类型。注意,一定是对象类型,非对象类型是不可以的。而且后面没有星号*。

例如:
Teacher* daPeng = [Teahcer new];
id simple = daPeng; // 正确
id value = 5; // 错误 不能表示非对象类型
id* value1 = daPeng; // 错误 id后面没有星号

我们把上面的例子,使用代码表示如下:

定义Teacher类

@interface Teacher : NSObject

- (void)haveInterview;

@end
@implementation Teacher

- (void)haveInterview {
    NSLog(@"1.很漂亮");
    NSLog(@"2.没有男朋友");
}
@end

定义Coder类

@interface Coder : NSObject

- (void)haveInterview;

@end
@implementation Coder

- (void)haveInterview {
    NSLog(@"1.沟通能力很强");
    NSLog(@"2.编码能力很强");
    NSLog(@"3.项目经验丰富");
    NSLog(@"4.薪资要求很低");
    NSLog(@"非常适合我们公司");
}
@end

main函数如下:

    // 大欢大鹏上场
    Coder* daHuan = [Coder new];
    Teacher* daPeng = [Teacher new];

    // 大欢成为面试官simple
    id simple = daHuan;
    [simple haveInterview];

    // 大鹏成为面试官simple
    simple = daPeng;
    [simple haveInterview];

打印结果:

1.沟通能力很强
2.编码能力很强
3.项目经验丰富
4.薪资要求很低
非常适合我们公司
1.很漂亮
2.没有男朋友

细心的同学可能已经注意到,我们两次向simple发送haveInterview消息,系统是如何区分当前应该执行哪一种haveInterview消息呢?

动态绑定:事实上,OC的系统总是跟踪当前对象所属的类,然后在运行时确定需要调用的方法,而不是在编译时。

因此,当simple接收到haeInterview消息时,系统首先去追踪当前simple表示的是属于哪个类的对象,进而确定当前该执行哪个类的方法。
所以便有了当大欢作为simple得到的是

1.沟通能力很强
2.编码能力很强
3.项目经验丰富
4.薪资要求很低
非常适合我们公司

而当大鹏作为simple的时候,得到的是

1.很漂亮
2.没有男朋友

三、编译时和运行时检查

在使用id类型表示的对象类型,在编译时是并没有确定该对象类型的,事实上这个时候也无法确定类型。就像来参加面试那个妹子,她并不认识大欢和大鹏,她只知道与他对话的是一个面试官(前面我们使用simple代号来表示)。直到开始面试时,才确定当前面试官simple到底是大鹏还是大欢。

也就是说,id表示的对象类型是在程序运行时检查出来的。

例如大欢喜欢在讨论技术的时候,会喜欢先给你一个不屑的眼神然后在给你耐心讲解这个知识点更深入的一层含义,我们把他的这种行为称为zhuangBiTeach,大鹏喜欢在课下给他的学生解决一些技术难题,我们把他的这种行为称为debugTeach。

这样我们需要把上面的两个类做如下修改:

1.教师类

@interface Teacher : NSObject

- (void)haveInterview;
- (void)debugTeach;

@end
@implementation Teacher

- (void)haveInterview {
    NSLog(@"1.很漂亮");
    NSLog(@"2.没有男朋友");
}

- (void)debugTeach {
    NSLog(@"大鹏帮你定位了bug并分析了原因");
    NSLog(@"然后梳理了一下思路,分分钟完美解决了bug");
}
@end

2.程序员类

@interface Coder : NSObject

- (void)haveInterview;
- (void)zhuangbiTeach;

@end
@implementation Coder

- (void)haveInterview {
    NSLog(@"1.沟通能力很强");
    NSLog(@"2.编码能力很强");
    NSLog(@"3.项目经验丰富");
    NSLog(@"4.薪资要求很低");
    NSLog(@"非常适合我们公司");
}

- (void)zhuangbiTeach {
    NSLog(@"大欢向你抛出了一个白眼");
    NSLog(@"哎哎,你们聊的这个层面太浅,让哥来给你迷途指引一下方向");

}
@end

在主函数中,做如下处理

    // 大鹏上场
    Teacher* daPeng = [Teacher new];

    // 大鹏成为面试官simple
    id simple = daPeng;

    // 此处编译时没错,运行时报错
    [simple zhuangbiTeach];

如上,[simple zhuangbiTeach]; 这行代码会报错。报错结果如下

-[Teacher zhuangbiTeach]: unrecognized selector sent to instance 0x1001057a0

也就是说Teacher类实例并不能响应zhuangbiTeach方法。

在程序编译阶段,因为系统无法确定simple表示的对象类型,系统只能确定zhuangbiTeach是一个有效的方法。所以没有报错。而zhuangbiTeach是大欢的技能,声明在Coder类中,当程序运行时,追踪simple所表示的对象是一个Teacher对象,而Teacher类并没有实现zhuangbiTeach方法,所以程序在运行时报错了。

id数据类型与静态类型

初学的小伙伴经常会问我,智哥,既然id类型可以表示任意类型的对象,那以后我就都用id类型来表示对象啦。当然,新手总是对新技术充满好奇,这个我非常理解。所以这个时候,我总会耐心的做如下解释,你咋不上天呢?

我不得不给大家讲一点哲学。需知万事皆有度,物极必反啊。没有什么是绝对的好,也没有什么是绝对的不好。好与不好是要分场景的。一个妹子跑到你面前说你好帅,然后把从小朋友手里抢的棒棒糖送给你,你能说她不好吗?

为了理解为什么不能一直用id类型,我们先理解两种类型,静态类型和动态类型。静态类型就是我们使用类名字来声明的对象指针,动态类型就是id类型。比如我们定义大鹏可以使用如下两种方式来表示他

1. id daPeng = [[Teacher alloc] init]; // 动态类型
2. Teacher* daPeng = [[Teacher alloc] init]; // 静态类型

我想我已经表述清楚什么是动态类型什么是静态类型了。

使用静态类型时,编译器可以尽可能的确保变量的用法在程序中始终保持一致。它可以在编译阶段检查来确定这个对象要调用的方法是否可以成功响应。否则将直接报错。也就是说,使用静态类型,可以更好的在编译阶段而不是运行阶段查错

另外id daPeng 和 Teacher* daPeng两种方式中,显然使用静态类型的可读性更高。所以,对于程序的自说明性也是有非常重大的意义的。

注意:由于id不是一个具体的类型,系统在编译阶段无法确定,所以id也不能使用点运算符访问属性。

动态类型的使用

动态类型的使用,得益于多态是被支持的。一般体现在多态类型作参数和做返回值两个方面。

再来一个小故事。大欢一直为自己的发型感到骄傲。但最近他想换一个新发型。所以他决定去找一个给力的发型师帮他设计一款新发型。故事就此展开。

先设计一个发型师Designer类

@interface Designer : NSObject

- (void)showHairStyle;

@end
@implementation Designer

- (void)showHairStyle {
    NSLog(@"不是每一位发型师都会设计发型");
}

@end

再设计一个大欢所属的Coder类,大欢需要有一个方法,就是寻找一位发型师,由于发型师可以是很多不同风格的发型师,所以使用id类型来表示不同风格的发型师

@interface Coder : NSObject

- (void)designHairStyle:(id)designer;

@end
// 为保证showHairStyle是可以被系统识别的,所以导入Designer.h
#import "Designer.h"
@implementation Coder

- (void)designHairStyle:(id)designer {

    [designer showHairStyle];
}

@end

Coder类,有一个designHairStyle方法,参数是一个id类型。表示需要一个设计师来设计发型。

那么,只要有一个对象,它继承自Designer类,就可以成为设计师,来帮助大欢设计发型。

所以,我们设计两个Designer的子类

1.男发型师
#import "Designer.h"

@interface ManDesigner : Designer

@end
@implementation ManDesigner

- (void)showHairStyle {
    NSLog(@"一款光滑的光头发型");
}
@end

2.女发型师

#import "Designer.h"

@interface WomanDesigner : Designer

@end
@implementation WomanDesigner

- (void)showHairStyle {
    NSLog(@"一款飘逸的长发及腰");
}
@end

main函数如下:
先导入两个头文件
#import "ManDesigner.h"
#import "WomanDesigner.h"

代码如下:

    Coder* daHuan = [[Coder alloc] init];

    ManDesigner* tom = [[ManDesigner alloc] init];
    WomanDesigner* lucy = [[WomanDesigner alloc] init];

    [daHuan designHairStyle:tom];
    [daHuan designHairStyle:lucy];

打印结果:

一款光滑的光头发型
一款飘逸的长发及腰

如果大欢对设计的发型不满意,可以再找一位发型师,只要这位发型师也实现了showHairStyle方法即可。

所以,多态的存在,让我们的程序可以设计的更加灵活。然而,在使用动态类型的时候,也会存在很多潜在风险。比如,我们使用id类型来表示一个设计师,如果这个设计师并不会设计发型(没有实现showHairStyle方法),当大欢调designHairStyle方法时,用程序就会报错。所以下节课我们来学习,如何规避这些风险。

如有疏漏,敬请指正,下次再见!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,639评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,093评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,079评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,329评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,343评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,047评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,645评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,565评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,095评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,201评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,338评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,014评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,701评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,194评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,320评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,685评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,345评论 2 358

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,654评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,681评论 18 139
  • 转自:http://blog.csdn.net/jackfrued/article/details/4492194...
    王帅199207阅读 8,537评论 3 93
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,265评论 11 349
  • 关于Qt开发,数据的临时存储及调用,可以使用C++编成之MVC设计模式。Model:模型数据 定义View:前端视...
    YBshone阅读 9,632评论 0 0