为什么Objective-C的对象“调用方法”叫做发消息。

来源:互联网 发布:java 函数的返回值 编辑:程序博客网 时间:2024/05/13 11:59

      Objective-C与C++,java等面向对象的语言类似,但是在使用Objective-C的类创建对象,并且这个对象“调用自身类的方法的时候”,我们叫做给对象发送一个消息,而不是调用方法,这就是Objective-C和C++不同的关键所在,因为该语言使用“消息结构”(message structure)而非“函数调用”(function calling)。消息和函数的区别,像下面一样。

   Messaging (Objective-C)

  

<span style="font-size:18px;background-color: rgb(255, 255, 255);">  <span style="color:#ff6666;">Object *obj=[[Object alloc] init];  [obj performWith:parameter1 and:parameter2]d;</span></span>

   Function calling (C++)

</pre><pre name="code" class="cpp"><span style="font-size:18px;color:#ff6666;"> Object *obj=new Object; obj->perform(parameter1,parameter2);</span>

     关键在于:使用消息结构的语言,其运行时所执行的代码由运行环境决定;而使用函数调用的语言由编译器决定,如果范例代码中调用的函数是多态的,那么运行时就要按照编译器生成的“虚函数表”(virtual table)进行查询到底该执行哪个函数的实现,而采用消息结构的语言,不论是否多态,总是在运行时才去查找所要执行的方法。实际上编译器甚至不关心接收消息的对象时何种类型。

    我们先看Objective-C类的定义(这个类的定义,在运行时库里,运行库的代码苹果已经开源)

</pre><pre name="code" class="objc"><span style="font-size:18px;color:#ff6666;">typedef struct objc_class *Class    //Class就是指向类结构体的指针</span></span>
<span style="font-size:18px;color:#ff6666;">struct objc_class {    struct objc_class *isa;//指向所属类的指针    struct objc_class *super_class;//指向父类的指针    const char *name;              //类的名字    long version;    long info;    long instance_size;    struct objc_ivar_list *ivars;//类的变量列表        struct objc_method_list **methodLists;//类的方法列表        struct objc_cache *cache;//每个类调用方法后,都会将此方法,缓存起来,便于下次调用的时候的查找    struct objc_protocol_list *protocols;//类的协议列表};</span>


  Objective-C对象的定义

<span style="font-size:18px;color:#ff0000;">typedef struct objc_object {    Class isa;//指向对象所属类的指针} *id;//id就是一个指向对象的指针</span>

   从上面的定义应该能看出Objective-C的对象结构体有一个isa(is a kind of class)的指针,这个指针指向对象所属的类,然而我们在看Objective-C类的结构体的定义,我们发现它仍然有一个isa指针,这说明Objective-C的类也是一个对象,类的isa指针指向这个类的元类,这个元类保存的这个类的类方法(类似于C++中静态方法)。




   Objective-C方法列表的定义

</pre><pre name="code" class="objc">
<span style="font-size:18px;"><span style="color:#ff0000;">typedef struct objc_method *Method;struct objc_method_list {    struct objc_method_list *obsolete;        int method_count;#ifdef __alpha__    int space;#endif    struct objc_method {        SEL method_name;        char *method_types;        IMP method_imp;    } method_list[1];/* variable length structure */};</span></span>
选择子的定义

</pre><pre name="code" class="objc"><span style="font-size:18px;color:#ff0000;">/*** Definition of a selector.  Selectors themselves are not unique, but** the sel_id is a unique identifier.*/typedef const struct objc_selector {  void *sel_id;  const char *sel_types;} *SEL;</span>
</pre><pre name="code" class="objc" style="font-size:18px;"><span style="color:#ff0000;">typedef struct objc_selector *SEL;typedef id (*IMP)(id, SEL, ...);</span>
     IMP就是一个指向函数的指针这个函数的返回值是id类型第一个参数时id类型代表调用方法的对象,第二个参数时一个指向选择器的结构体后面的参数是我们要给方法传递的参数

    Objective-C的类里的所有方法,都可以表示成这个形式,对于objc_selector(选择器),顾名思义,是一个方法选择器,如何选择呢?应该是每个类都把他本身的方法放到一个哈希表里,方法名就是键,值就是函数指针IMP,这个就是类的方法列表会把选择子的名称映射到相关的方法上。


    例如NSString类可以响应lowercaseStrng,uppercaseString等选择子方法,方法列表把每个选择子都映射到不同的IMP上


   Objective-C运行时系统提供了几种能够操作这张表的方法,比如我们可以任意交换两个方法的实现


    详细的实践,这里就不赘述了,我这里只讲自己理解的原理,仔细想想,这底层的实现太复杂了,有些东西还是编译器处理的

    在对象上调用方法是Objective-C的常用功能,这用Objective-C的术语来说这叫"传递消息",消息叫"名称"或者"选择子",还可能有返回值,由于Objective-C是C的超集,而C语言掉用函数的方式是静态绑定,也就是说在编译期就能确定调用的函数,如果要实现C语言的"动态绑定"则应该使用函数指针。

    向对象发送消息应该这样写

<span style="font-size:18px;color:#ff6666;">id returnValue=[someObject messageName:parameter1 AndName2:parameter2];</span>
</pre><p><span style="font-size:18px;">在Objective-C中所有对象的方法调用都转换成下面这个函数的调用:</span></p><p><span style="font-size:18px;"></span><pre name="code" class="objc"><span style="color:#ff6666;">id  <span style="font-family: Arial, Helvetica, sans-serif;">objc_msgSend(id self,SEL cmd,...);</span></span>
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">这是个“参数可变的函数“能接受两个或两个以上的参数,第一个表示接收者,第二个表示选择子,第三个是一个可变参数,是传入的选择子的参数</span>
编译器会把上面对象的函数调用转换成

<span style="color:#ff6666;">id returnValue=objc_msgSend(someObject,@selsector(messageName:AndName2:),parameter1,parmeter2);</span>
</pre><pre name="code" class="objc">
</pre>   objc_msgSend会在对象所属类的“方法列表”中找寻与选择子一样的方法,如果没找到,则沿着继承体系继续寻找,等找到合适的方法后进行跳转。如果最终都没找到符合要求的方法,则将执行“消息转发”。<p></p><p><span style="font-size:18px">  消息转发</span></p><p><span style="font-size:18px">1.看所属的类,是否能动态的添加一个方法,响应这个选择子,如果没有进入第二步</span></p><p><span style="font-size:18px">2.这时进入到这一步,请接受者看看,是否有其他对象可以响应这个消息,如果有,则运行时系统,会把消息转发给那个对象,于是消息转发过程结束。若没有备援接受者,则进入第三步,</span></p><p><span style="font-size:18px">3.运行时系统会把所有与消息相关的细节都封装到NSInvocation对象中,在给接受者最后一次机会,令其设法解决未解决的问题。</span></p><p><span style="font-size:18px">      这样来说,这样的动态调用方法,似乎在效率上会比C++的静态绑定慢,但是,objc_msgSend会将匹配的结果缓存在类的“快速映射表中”,在调用之前,会多执行一步,先在类的缓存了寻找方法.这样在效率上不会输给静态绑定很多,这样使得Objective-C成为一门真正动态的语言,给这门语言很多灵活的性质。</span></p><p></p><p><span style="font-size:18px"><strong></strong></span></p><pre name="code" class="objc">











0 0
原创粉丝点击