Bird * aBird = [[Bird alloc] init];
[aBird fly];
中对 fly 的调用,编译器通过插入一些代码,将之转换为对方法具体实现 IMP 的调用,这个 IMP 是通过 在 Bird 的类结构中的方法链表中查找名称为 fly 的 选标 SEL 对应的具体方法找到实现的。
消息函数 obj_msgSend:
编译器会将消息转换为对消息函数 objc_msgSend 的调用,该函数有两个主要的参数:消息接收者 id 和
消息对应的方法选标 SEL, 同时接收消息中的任意参数: id objc_msgSend(id theReceiver, SELtheSelector, ...)
如上面的消息 [aBird fly]会被转换为如下形式的函数调用: objc_msgSend(aBird, @selector(fly));
该消息函数做了动态绑定所需要的一切工作:
1,它首先找到 SEL 对应的方法实现 IMP。因为不同的类对同一方法可能会有不同的实现,所以找到的 方法实现依赖于消息接收者的类型。
2, 然后将消息接收者对象(指向消息接收者对象的指针)以及方法中指定的参数传递给方法实现 IMP。
3, 最后,将方法实现的返回值作为该函数的返回值返回。
在方法中可以通过 self 来引用消息接收者对象,通过选标_cmd 来引用方法本身。在下面的例 子中,_cmd 指的是 strange 方法,self 指的收到 strange 消息的对象。
1 ? 2 - strange { 3 id target = getTheReceiver(); 4 SEL method = getTheMethod(); 5 if (target == self || mothod == _cmd) 6 return nil; 7 return [target performSelector:method]; 8 }
前面说了,objc_msgSend 会根据方法选标 SEL 在类结构的方法列表中查找方法实现 IMP。这里头有一 些文章,我们在前面的类结构中也看到有一个叫 objc_cache *cache 的成员,这个缓存为??高效率而存在 的。每个类都有一个独立的缓存,同时包括继承的方法和在该类中定义的方法。
下面来剖析一段苹果官方运行时源码:
1 static Method look_up_method(Class cls, SEL sel, 2 BOOL withCache, BOOL withResolver) 3 { 4 Method meth = NULL; 5 if (withCache) { 6 meth = _cache_getMethod(cls, sel, &_objc_msgForward_internal); 7 if (meth == (Method)1) { 8 // Cache contains forward:: . Stop searching. 9 return NULL; 10 } 11 } 12 if (!meth) meth = _class_getMethod(cls, sel); 13 if (!meth && withResolver) meth = _class_resolveMethod(cls, sel); 14 return meth; 15 }
通过分析上面的代码,可以看到,查找时:
1,首先去该类的方法 cache 中查找,如果找到了就返回它;
2,如果没有找到,就去该类的方法列表中查找。如果在该类的方法列表中找到了,则将 IMP 返回,并将 它加入 cache 中缓存起来。根据最近使用原则,这个方法再次调用的可能性很大,缓存起来可以节省下次 调用再次查找的开销。3,3,如果在该类的方法列表中没找到对应的 IMP,在通过该类结构中的 super_class 指针在其父类结构的方法列表中去查找,直到在某个父类的方法列表中找到对应的 IMP,返回它,并加入 cache 中;
4,如果在自身以及所有父类的方法列表中都没有找到对应的 IMP,则看是不是可以进行动态方法决议(后 面有专文讲述这个话题);
5,如果动态方法决议没能解决问题,进入下面要讲的消息转发流程。
原文:http://www.cnblogs.com/H7N9/p/4893323.html