OC的多态(运行时)转

在介绍多态之前,我们来回顾下面向对象的三个特性,封装、继承、多态

封装:就是对类中的一些方法、字段进行保护,不被外界访问,有一种权限控制功能,例如OC中有@public、@protected、@private、@package,默认使用@private

继承:为了代码的重用,子类可以继承父类的方法和变量

多态:多态是指同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。它是面向对象程序设计(OOP)的一个重要特征,动态类型能使程序直到执行时才确定对象的所属类,其具体引用的对象在运行时才能确定。动态绑定能使程序直到运行时才确定调用对象的实际方法。

C++中的多态性具体体现在运行和编译两个方面,编译时多态是静态多态(重载、模版),在编译时就可以确定对象使用的形式,运行时多态是动态多态(虚函数抽象类,覆盖)。

C++使用虚函数(虚函数表)来实现动态绑定,当基类对象的指针(或引用)指向派生类的对象时候,实际调用的是派生类相应的函数。

Objective-c 是动态语言,所以它具有动态类型和动态绑定的特性。Objective-c系统总是跟踪对象所属的类。对于类型的判断和方法的确定都是在运行时进行。那Objective-c是怎么样实现多态特性的呢?

二 Objective-c多态

首先看下面代码

draw.h文件

@interfaceDraw :NSObject

@property(nonatomic,strong)NSString*name;

- (void) Print;

- (void) draw;

@end

draw.m文件

#import"Draw.h"

@implementationDraw

@synthesizename;

- (id) init

{

if(self= [superinit])

{

self.name=@"Draw Demo";

}

returnself;

}

- (void) draw

{

NSLog(@"Draw::draw.......");

}

- (void) Print

{

NSLog(@"i am  %@.",self.name);

}

@end

cricle.h文件

#import"Draw.h"

@interfaceCircle :Draw

@end

circle.m文件

#import"Circle.h"

@implementationCircle

- (void) draw

{

NSLog(@"%@:draw circle",self.name);

}

@end

Retangle.h文件

#import"Draw.h"

@interfaceRetangle :Draw

@end

Retangle.m文件

#import"Retangle.h"

@implementationRetangle

- (void) draw

{

[superdraw];//通过super关键字可以调用基类的draw函数

NSLog(@"%@:draw retangle",self.name);

}

@end

我们定义了一个Draw基类,里面有一个数据成员name,和两个函数成员draw和Print,Circle和Retangle是从Draw派生的两个类,他们重写了基类Draw的draw方法。

代码使用

Draw* base = [[Circlealloc]init];

[basedraw];//draw circle

NSLog(@"address:%@",base);

base = [[Retanglealloc]init];

[basedraw];//draw retangle

NSLog(@"address:%@",base);

[basePrint];

输出结果

2014-04-09 15:34:41.648 duotaidemo[7718:303] Draw Demo:draw circle

2014-04-09 15:34:41.673 duotaidemo[7718:303] address:

2014-04-09 15:34:41.674 duotaidemo[7718:303] Draw::draw.......

2014-04-09 15:34:41.674 duotaidemo[7718:303] Draw Demo:draw retangle

2014-04-09 15:34:41.675 duotaidemo[7718:303] address:

2014-04-09 15:34:41.676 duotaidemo[7718:303] i am  Draw Demo.

使用基类的指针分别指向创建的两个派生类对象,然后分别调用各自的draw函数,通过输出结果可以发现他们调用的是各自的draw方法。由于Retangele没有重写基类的Print函数,所有使用[base Print]调用的是基类的方法。同时通过address的输出发现base指向了两个不同的对象。

小结:

1.与C++ 的多态相比,在Objective-c中是没有virtual关键字的,默认情况下只要子类重写了父类的方法就实现了覆盖(这一点和java类似),在Objective-c中同一类中的函数是不能被重载的。

2.在Objective-c中,通过super关键字可以调用基类的函数,这个在C++中是没有的,在C++中可通过作用域运算符访问基类成员。

除了上面的调用方式外,我们也可以这样:

idbase = [[Circlealloc]init];

[basedraw];//draw circle

NSLog(@"address:%@",base);

base = [[Retanglealloc]init];

[basedraw];//draw retangle

NSLog(@"address:%@",base);

[basePrint];

其输出结果和上面是一样的

既然Objective-c中没有像C++一样的虚函数表,那它的多态是怎么实现的?它的类型系统是怎么样构建起来的呢?继续往下看吧!

三  类对象

虽然Objective-c没有虚函数表,但是它有一个根类NSObject,下面让我们探究一下这个根类是个什么东东。

objc.h文件中关于NSObject的定义

@interfaceNSObject

{

Class isaOBJC_ISA_AVAILABILITY;

}

typedefstructobjc_class*Class;

typedefstructobjc_object {

Class isa;

} *id;

typedefstructobjc_selector*SEL;

typedefid(*IMP)(id,SEL, ...);

详见:http://opensource.apple.com/source/objc4/objc4-493.9/runtime/objc.h

通过上面的定义我们可以知道以下事实:

1.Class isa 是NSObject类的第一个数据成员。

2.Class 是一个指针,它指向一个objc_class的结构体。

3.id 类型是一个指针,它指向一个objc_object的结构体,该结构体只有一个成员即Class isa;

4.id 类型是一个指针,它指向一个存有objc_class的结构对象的指针的指针。

3.1 isa介绍

以下是苹果官方文档对isa的介绍说明:

Every object is connected to the run-time system through its isa instance variable, inherited from the NSObject class. isa identifies the object's class; it points to a structure that's compiled from the class definition. Through isa, an object can find whatever information it needs at run timesuch as its place in the inheritance hierarchy, the size and structure of its instance variables, and the location of the method implementations it can perform in response to messages.

实例变量是通过isa成员链接到运行时系统环境中的,任意NSObject的子类都会继承NSObject的isa成员,而且当NSObject的子类实例化对象时,isa实例变量永远是对象的第一个实例变量。isa指向该对象的类对象,它是实例和类对象连接的桥梁。

实例变量和类对象的关联,如下图所示:

下面是类对象(objc_class)的结构体

structobjc_class {

Class isa;                                            /* metaclass */                Class super_class                                  /* 父类的地址 */constchar*name                                  /*  类名称  */longversion                                        /*  版本    */longinfo                                            /*  类信息  */longinstance_size                                /*  实例大小  */structobjc_ivar_list *ivars                    /*  实例参数列表*/structobjc_method_list **methodLists/*  方法列表  */structobjc_cache *cache                      /*  方法缓存  */structobjc_protocol_list *protocols/*  protocol链表*/

} ;

在Objective-C中类也是一种对象,而且在程序运行时一直存在。类对象是一个根据类定义生成的一个结构体,里面存储了类的基本信息, 如:类的大小,类的名称,类的版本以及消息与函数的映射表等信息。类对象所保存的信息在程序编译时确定,在程序启动 时加载到内存中。

3.2 id介绍

由上面的定义我们知道,id类型是一个指向类对象的指针的指针。在Objective-c中,id类型是一种通用的指针类型,id类型可以用来指向属于任何类的对象(只要该对象是属于NSObject即成体系)。

id类型的使用如下图所示:

在使用id类型的时候要注意:

1. id类型本事是一个指针类型,在使用时就不用加*号了,例如上面的例子idbase = [[Circlealloc]init];

2.id类型是通用指针类型,弱类型,编译时不进行类型检查

Objective-C可以将对象分为id类型和静态类型,如果不涉及到多态,尽量使用静态类型。

在上的例子中我们使用了两种方式来调用派生类函数,第一种使用的即使静态类型,第二种使用的是id动态类型。在写代码时候,尽量使用静态类型,静态类型可更好的在编译阶段而不是运行阶段指出错误,同时能够提高程序的可读性。

四 小结

实例变量中isa成员用于保持其类对象在内存的地址,类对象对于所有实例来说在内存中只有一份副本,任何一个实例都可以通过 isa成员,访问类对象所保持的类的信息,isa成员可以通过类对象获得当前实例可以访问的消息列表,以及消息对应的函数地址。

Objecive-c使用类对象的形式来实现运行多态,每个对象都保存其类对象的地址,类对象中保存了类的基本信息。类对象是进行动态创建(反射),动态识别,消息传递等机制的基础。

那么上面的程序中,函数的调用过程时怎么样利用类对象的呢?

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

推荐阅读更多精彩内容