Objective-C 2.0 with Cocoa Foundation---NSObject的奥秘(3)

  图6-2,选择执行断点

  第六步,选择Xcode上面的菜单的“Run”,然后选择“Debuger” ,在Debuger窗口里面选择“Build and Go”。

  好的,大家就停在这里,不要做其他的操作,我们把程序中断在程序几乎执行到最后的断点上,我们将要通过Debuger来看看objecsive-C内部究竟发生了什么样的奇妙的魔法。

  注意

  在从编译到执行的过程当中,会出现一些警告。由于本章程序指示用来阐述一些NSobjecs内部的东西,所以请忽略掉这些警告。当然,我们在写自己的程序的时候,编译产生的警告一般是不能被忽略的。

  6.3,超类方法的调用

  我们现在打开“06-NSobjecs.m”文件,发现下面的代码:

SEL setLegsCount_SEL = @selector(setLegsCount:);
IMP cattle_setLegsCount_IMP = [cattle methodForSelector:setLegsCount_SEL];
IMP redBull_setLegsCount_IMP = [redBull methodForSelector:setLegsCount_SEL];

  这一段代码,对同学们来说不是什么新鲜的内容了,我们在第5章里面已经讲过,这个是SEL和IMP的概念。我们在这里取得了cattle对象和redBull对象的setLegsCount:的函数指针。

  如果大家现在已经不在Debuger里面的话,那么请选择Xcode菜单里面的,“Run”然后选择“Debuger” 。

  我们注意到在Debuger里面,cattle_setLegsCount_IMP的地址和redBull_setLegsCount_IMP是完全一样的,如图6-3所示:

objecsive-C 2.0 with Cocoa Foundation--- 6,NSobjecs的奥秘

 

  图6-3,cattle_setLegsCount_IMP和redBull_setLegsCount_IMP的地址。

  注意

  由于环境和执行的时候的内存情况不同,所以同学们的电脑上显示的地址的数值可能和图6-3的数值不一样。

  他们的地址完全一样,说明他们使用的是相同的代码段。这种结果是怎样产生的呢?大家请打开“MyNSobjecs.h”,参照下列代码:

struct my_objc_class {     
     meteClass           class_pointer;         
     struct my_objc_class*  super_class;            
     const char*         name;                 
     long                version;               
     unsigned long       info;                  
     long                instance_size;          
     struct objc_ivar_list* ivars;              
     struct objc_method_list*  methods;          
     struct sarray *    dtable;                  
     struct my_objc_class* subclass_list;           
     struct my_objc_class* sibling_class;
     struct objc_protocol_list *protocols;         
     void* gc_objecs_type;
};

  笔者在这里把开源代码的名字的定义加上了“my_”前缀,仅仅是为了使编译不出现问题。这段代码实际上就是Class的实际上定义的部分。

  我们注意到这里的methods变量,里面包存的就是类的方法名字(SEL)定义,方法的指针地址(IMP)。当我们有执行

IMP cattle_setLegsCount_IMP = [cattle methodForSelector:setLegsCount_SEL];

  的时候,runtime会通过dtable这个数组,快速的查找到我们需要的函数指针,查找函数的定义如下:

__inline__ IMP
objc_msg_lookup(id receiver, SEL op)
{
  if(receiver)
    return sarray_get(receiver- >class_pointer->dtable, (sidx)op);
  else
    return nil_method;
objecsive-C 2.0 with Cocoa Foundation--- 6,NSobjecs的奥秘

  好的,现在我们的cattle_setLegsCount_IMP没有问题了,那么redBull_setLegsCount_IMP怎么办?在Bull类里面我们并没有定义实例方法setLegsCount:,所以在Bull的Class里面,runtime难道找不到setLegsCount:么?答案是,是的runtime直接找不到,因为我们在Bull类里面根本就没有定义setLegsCount:。

  但是,从结果上来看很明显runtime聪明的找到了setLegsCount:的地址,runtime是怎样找到的?答案就在:

struct my_objc_class*  super_class;     

  当寻找失败了之后,runtime会去Bull类的超类cattle里面去寻找,在cattle类里面runtime找到了setLegsCount:的执行地址入口,所以我们得到了redBull_setLegsCount_IMP。 redBull_setLegsCount_IMP和cattle_setLegsCount_IMP都是在Cattle类里面定义的,所以他们的代码的地址也是完全一样的。

  我们现在假设,如果runtime在cattle里面也找不到setLegsCount:呢?没有关系,cattle里面也有超类的,那就是NSobjecs。所以runtime会去NSobjecs里面寻找。当然,NSobjecs不会神奇到可以预测我们要定义setLegsCount:所以runtime是找不到的。

  在这个时候,runtime 并没有放弃最后的努力,再没有找到对应的方法的时候,runtime会向对象发送一个forwardInvocation:的消息,并且把原始的消息以及消息的参数打成一个NSInvocation的一个对象里面,作为forwardInvocation:的唯一的参数。 forwardInvocation:本身是在NSobjecs里面定义的,如果你需要重载这个函数的话,那么任何试图向你的类发送一个没有定义的消息的话,你都可以在forwardInvocation:里面捕捉到,并且把消息送到某一个安全的地方,从而避免了系统报错。

  笔者没有在本章代码中重写forwardInvocation:,但是在重写forwardInvocation:的时候一定要注意避免消息的循环发送。比如说,同学们在A类对象的forwardInvocation里面,把A类不能响应的消息以及消息的参数发给B类的对象;同时在B类的forwardInvocation里面把B类不能响应的消息发给A类的时候,容易形成死循环。当然一个人写代码的时候不容易出现这个问题,当你在一个工作小组里面做的时候,如果你重写forwardInvocation:的时候,需要和小组的其他人达成共识,从而避免循环调用。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值