消息
- 体会苹果官方文档中的 messages aren’t bound to method implementations until Runtime。消息直到运行时才会与方法实现进行绑定。
- 我理解的是:源码通过预处理 ->编译->链接->汇编->生成可执行文件,在由shell(暂且理解为shell)解释执行的时候才与方法的实现进行绑定,
- 如何绑定:如果是实例方法每个实例的isa指针指向该实例对应的类,在这个类中有一个分发表,在这个表中存储着一个SEL(理解为方法编号)对应一个IMP(函数指针),通过SEL找到对应的IMP,再将IP指针指向该函数地址执行
1、objc_msgSend
- 这里要清楚一点,objc_msgSend 方法看起来好像返回了数据,其实objc_msgSend 从不返回数据,而是你的方法在运行时** 实现 ** ** 被调用后 **才会返回数据。下面详细叙述消息发送的步骤(如下图):
消息转发顺序
- 首先检测这个* selector *是不是要忽略,比如 Mac OS X 开发,有了垃圾回收就不理会 retain,release 这些函数。
检测这个 selector 的 target 是不是 nil,Objc 允许我们对一个 nil 对象执行任何方法不会 Crash,因为运行时会被忽略掉
如果上面两步都通过了,那么就开始查找这个类的实现 IMP,先从 cache 里查找,如果找到了就运行对应的函数去执行相应的代码。
如果 cache 找不到就找类的方法列表中是否有对应的方法。
如果类的方法列表中找不到就到父类的方法列表中查找,一直找到 NSObject 类为止。
如果还找不到,就要开始进入动态方法解析了
消息转发函数,编译器会根据情况在以下四个方法中选择一个调用
/** 如果消息是传递给父类,那么会调用名字带有 Super 的函数,如果消息返回值是数据结构而不是简单值时,会调用名字带有 stret 的函数。 */
objc_msgSend
objc_msgSend_stret
objc_msgSendSuper
objc_msgSendSuper_stret
2、方法中的隐藏参数
- ** 我们经常用到关键字 self ,但是 self 是如何获取当前方法的对象呢? **
其实,这也是 Runtime 系统的作用,self 实在方法运行时被动态传入的。当 objc_msgSend 找到方法对应实现时,它将直接调用该方法实现,并将消息中所有参数都传递给方法实现,同时,它还将传递两个隐藏参数: - 接受消息的对象(self 所指向的内容,当前方法的对象指针)
- 方法选择器(_cmd 指向的内容,当前方法的 SEL 指针)
这两个参数中, self更实用。它是在** 方法实现中 访问 消息接收者对象 的实例变量的 途径 **(不是很理解)
3、获取方法的地址
- NSObject 类中有一个实例方法:methodForSelector,你可以用它来获取某个方法选择器对应的 IMP ,举个例子:
/* 定义一个函数指针 */
void (*setter)(id, SEL, BOOL);
int i;
/* 给函数指针赋值 */
setter = (void (*)(id, SEL, BOOL))[target methodForSelector:@selector(setFilled:)];
/* 通过函数指针调用1000次这个方法 */
for ( i = 0 ; i < 1000 ; i++ ){
setter(targetList[i], @selector(setFilled:), YES);
}
** 注意:上面用到的methodForSelector:方法是由 Runtime 系统提供的,而不是 Objc 自身的特性 **