Object-C的函数调用机制详解--消息

上传人:ths****59 文档编号:44922299 上传时间:2018-06-14 格式:DOCX 页数:11 大小:38.50KB
返回 下载 相关 举报
Object-C的函数调用机制详解--消息_第1页
第1页 / 共11页
Object-C的函数调用机制详解--消息_第2页
第2页 / 共11页
Object-C的函数调用机制详解--消息_第3页
第3页 / 共11页
Object-C的函数调用机制详解--消息_第4页
第4页 / 共11页
Object-C的函数调用机制详解--消息_第5页
第5页 / 共11页
点击查看更多>>
资源描述

《Object-C的函数调用机制详解--消息》由会员分享,可在线阅读,更多相关《Object-C的函数调用机制详解--消息(11页珍藏版)》请在金锄头文库上搜索。

1、ps:Object-C 和 C,C+,java 的函数调用机制还是有所区别的,其完全的 runtime 的调用方式是其实现消息机制的本质内容!在入门级别的 ObjC 教程中,我们常对从 C+或 Java 或其他面向对象语言转过来的程序员说,ObjC 中的方法调用(ObjC 中的术语为消息)跟其他语言中的方法调用差不多,只是形式有些不同而已。譬如+ 中的:Bird * aBird = new Bird();aBird-fly();在 ObjC 中则如下:Bird * aBird = Bird alloc init;aBird fly;初看起来,好像只是书写形式不同而已,实则差异大矣。+中的方法调

2、用可能是动态的,中的方法调用可能是动态的,也可能是静态的;而也可能是静态的;而 ObjC 中的消息都为动态的。中的消息都为动态的。下文将详细介绍为什么是动态的,以及编译器在这背后做了些什么事情。要说清楚消息这个话题,我们必须先来了解三个概念 Class, SEL, IMP,它们在objc/objc.h 中定义:typedef struct objc_class *Class;typedef struct objc_object Class isa; *id;typedef struct objc_selector *SEL; typedef id (*IMP)(id, SEL, .);Clas

3、s 的含义的含义Class 被定义为一个指向被定义为一个指向 objc_class 的结构体指针,这个结构体表示每一个类的类结构的结构体指针,这个结构体表示每一个类的类结构。而 objc_class 在 objc/objc_class.h 中定义如下:struct objc_class struct objc_class super_class; /*父类父类*/const char *name; /*类名字*/long version; /*版本信息*/long info; /*类信息*/long instance_size; /*实例大小*/struct objc_ivar_list *i

4、vars; /*实例参数链表*/struct objc_method_list *methodLists; /*方法链表方法链表*/struct objc_cache *cache; /*方法缓存*/struct objc_protocol_list *protocols; /*协议链表协议链表*/;由此可见,Class 是指向类结构体的指针,该类结构体含有一个指向其父类类结构的指针,该类方法的链表,该类方法的缓存以及其他必要信息。NSObject 的 class 方法就返回这样一个指向其类结构的指针。每一个类实例对象的第一个实例变量是一个指向该对象的类结构的指针,叫做 isa。通过该指针,对

5、象可以访问它对应的类以及相应的父类。如图一所示:如图一所示,圆形所代表的实例对象的第一个实例变量为如图一所示,圆形所代表的实例对象的第一个实例变量为 isa,它指向该类的类结构,它指向该类的类结构 The objects class。而该类结构有一个指向其父类类结构的指针。而该类结构有一个指向其父类类结构的指针 superclass, 以及自身消息以及自身消息名称名称(selector)/实现地址实现地址(address)的方法链表。的方法链表。方法的含义:方法的含义:注意这里所说的方法链表里面存储的是方法链表里面存储的是 Method 类型类型的。图一中 selector 就是指就是指 Me

6、thod 的的 SEL, address 就是指就是指 Method 的的 IMP。 Method 在头文件 objc_class.h 中定义如下:typedef struct objc_method *Method;typedef struct objc_ method SEL method_name;/方法名称方法名称char *method_types;/方法参数类型方法参数类型IMP method_imp;/方法实现的函数指针方法实现的函数指针;一个方法一个方法 Method,其包含一个方法选标,其包含一个方法选标 SEL 表示该方法的名称,一个表示该方法的名称,一个 types 表示

7、表示该方法参数的类型,一个该方法参数的类型,一个 IMP - 指向该方法的具体实现的函数指针。指向该方法的具体实现的函数指针。SEL 的含义:的含义:在前面我们看到方法选标 SEL 的定义为:typedef struct objc_selector *SEL; 它是一个指向它是一个指向 objc_selector 指针,表示方法的名字指针,表示方法的名字/签名。签名。如下所示,打印出 selector。-(NSInteger)maxIn:(NSInteger)a theOther:(NSInteger)breturn (a b) ? a : b;NSLog(“SEL=%s“, selector

8、(maxIn:theOther:);输出:输出:SEL=maxIn:theOther:不同的类可以拥有相同的 selector,这个没有问题,因为不同类的实例对象performSelector 相同的 selector 时,会在各自的消息选标(selector)/实现地址(address) 方法链表中根据根据 selector 去查找具体的方法实现去查找具体的方法实现 IMP, 然后用这个方法实现去执行具体的实现代码。这是一个动态绑定的过程,在编译的时候,我们不知道最终会执行哪一些代码,这是一个动态绑定的过程,在编译的时候,我们不知道最终会执行哪一些代码,只有在执行的时候,通过只有在执行的时候

9、,通过 selector 去查询,我们才能确定具体的执行代码。去查询,我们才能确定具体的执行代码。IMP 的含义:的含义:在前面我们也看到 IMP 的定义为:typedef id (*IMP)(id, SEL, .);根据前面 id 的定义,我们知道 id 是一个指向 objc_object 结构体的指针,该结构体只有一个成员 isa,所以任何继承自任何继承自 NSObject 的类对象都可以用的类对象都可以用 id 来指代,因为来指代,因为 NSObject 的第一个成员实例就是的第一个成员实例就是 isa。至此,我们就很清楚地知道 IMP 的含义:IMP 是一个函数指针,这个被指向的函数包

10、含是一个函数指针,这个被指向的函数包含一个接收消息的对象一个接收消息的对象 id(self 指针指针), 调用方法的选标调用方法的选标 SEL (方法名方法名),以及不定个数的方法,以及不定个数的方法参数,并返回一个参数,并返回一个 id。也就是说。也就是说 IMP 是消息最终调用的执行代码,是方法真正的实现代是消息最终调用的执行代码,是方法真正的实现代码码 。我们可以像在语言里面一样使用这个函数指针。我们可以像在语言里面一样使用这个函数指针。NSObject 类中的 methodForSelector:方法就是这样一个获取指向方法实现获取指向方法实现 IMP 的指针的指针,methodFor

11、Selector:返回的指针和赋值的变量类型必须完全一致,包括方法的参数类型和返回值类型。下面的例子展示了怎么使用指针来调用 setFilled:的方法实现:void (*setter)(id, SEL, BOOL);int i;setter = (void(*)(id, SEL, BOOL)target methodForSelector:selector(setFilled:);for (i = 0; i 1000; i+)setter(targetListi, selector(setFilled:), YES);使用使用 methodForSelector:来避免动态绑定将减少大部分消

12、息的开销,但是这只有在指定:来避免动态绑定将减少大部分消息的开销,但是这只有在指定的消息被重复发送很多次时才有意义,例如上面的的消息被重复发送很多次时才有意义,例如上面的 for 循环。循环。注意,methodForSelector:是 Cocoa 运行时系统的提供的功能,而不是 Objective-C 语言本身的功能。消息调用过程:消息调用过程:至此我们对 ObjC 中的消息应该有个大致思路了:示例Bird * aBird = Bird alloc init;aBird fly;中对 fly 的调用,编译器通过插入一些代码,将之转换为对方法具体实现 IMP 的调用,这个 IMP 是通过在 B

13、ird 的类结构的类结构中的方法链表中方法链表中查找名称为名称为 fly 的的 选标选标 SEL 对应的具体具体方法实现方法实现找到的。上面的思路还有一些没有提及的话题,比如说编译器插入了什么代码,如果在方法链表中没有找到对应的 IMP 又会如何,这些话题在下面展开。消息函数消息函数 obj_msgSend:编译器会将消息转换为对消息函数将消息转换为对消息函数 objc_msgSend 的调用的调用,该函数有两个主要的参数:消息接收者 id 和消息对应的方法选标 SEL, 同时接收消息中的任意参数:id objc_msgSend(id theReceiver, SELtheSelector,

14、.)如上面的消息如上面的消息 aBird fly会被转换为如下形式的函数调用:会被转换为如下形式的函数调用:objc_msgSend(aBird, selector(fly);该消息函数做了动态绑定所需要的一切工作:该消息函数做了动态绑定所需要的一切工作:1,它首先找到,它首先找到 SEL 对应的方法实现对应的方法实现 IMP。因为不同的类对同一方法可能会。因为不同的类对同一方法可能会有不同的实现,所以找到的方法实现依赖于消息接收者的类型。有不同的实现,所以找到的方法实现依赖于消息接收者的类型。2, 然后将消息接收者对象然后将消息接收者对象(指向消息接收者对象的指针指向消息接收者对象的指针)以

15、及方法中指定的参以及方法中指定的参数传递给方法实现数传递给方法实现 IMP。3, 最后,将方法实现的返回值作为该函数的返回值返回。最后,将方法实现的返回值作为该函数的返回值返回。编译器会自动插入调用该消息函数 objc_msgSend 的代码,我们无须在代码中显示调用该消息函数。当 objc_msgSend 找到方法对应的实现时,它将直接调用该方法实现,并将消息中所有的参数都传递给方法实现,同时,它还将传递两个隐藏的参数:消息的接收者以及方法名称 SEL。这些参数帮助方法实现获得了消息表达式的信息。它们被认为是”隐藏“的是因为它们并没有在定义方法的源代码中声明,而是在代码编译时是插入方法的实现

16、中的。尽管这些参数没有被显示声明,但在源代码中仍然可以引用它们(就象可以引用消息接收者对象的实例变量一样)。在方法中可以通过 self 来引用消息接收者对象,通过选标_cmd来引用方法本身。在下面的例子中,_cmd 指的是 strange 方法,self 指的收到 strange 消息的对象。- strangeid target = getTheReceiver();SEL method = getTheMethod();if (target = self | mothod = _cmd)return nil;return target performSelector:method;在这两个参数中,self 更有用一些。实际上

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 行业资料 > 其它行业文档

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号