runtime总结二之消息机制(包括消息转发,消息交换的黑魔法)

runtime的消息机制

前面提到过编译器最终会把我们的消息发送转化为函数调用

  • 消息发送 [object sendMassage]

    首先编译器会在运行时将上面的例子转化为objc_msgSend(obj,@selector(sendMassage))这个函数,转换的时候除了方法本身的参数之外,还有两个隐藏的参数一个是id类型的,代表对象的类型,还是一个是SEL类型的,是函数对应的方法的编号,接下来就会按照下面的流程来调用这个方法

  1. 通过obj的isa指针找到其所对应的类。
  2. 通过SEL先去类的cache列表中找这个方法,如果就去找方法的实现,不存在,进入第3步
  3. 去类的method列表中找,如果就去找方法的实现,没有找到,根据类中的superclass指针去父类中找,一直到NSObject.
找到了方法之后就要去找方法的实现,那么如何找方法的实现呢,runtime提供了两种方式
IMP class_getMethodImplementation(Class cls, SEL name);
IMP method_getImplementation(Method m)
在id objc_msgSend(id self, SEL _cmd, ...) {
  Class class = object_getClass(self);
  IMP imp = class_getMethodImplementation(class, _cmd);
  return imp ? imp(self, _cmd, ...) : 0;
}

可以看到这个方法中的第二行代码imp,可以通过这个imp来查找这个方法的实现,要是没有找到,runtime给我们提供了三次机会让我们的程序不会崩溃,也就是下面要提到的动态方法解析和消息转发(消息重定向,消息转发)

* 动态方法解析

resolveInstanceMethod或者resolveClassMethod给你提供一次添加方法实现的机会

下面例子,在student类中有一个没有实现的write方法,但是没有实现,如果我们掉用它,会因为找不到实现程序崩溃,有下面的挽救措施

//在这个c语言函数添加方法的实现
void test(){
    NSLog(@"我是动态添加的方法");
}
//然后实现这个方法,当这个方法返回no时或者没有实现时会进入消息转发
+(BOOL)resolveInstanceMethod:(SEL)sel{
    if (sel == @selector(write)) {
        class_addMethod([self class], sel, (IMP)test, "[email protected]:");
    }
    return [super resolveInstanceMethod:sel];
}

如你添加了函数实现并且返回了yes,那么就会调用。不会进入下面的消息转发,否则进入下面的消息转发

  • 消息转发
消息转发分为两步,一次是消息的重定向,返回一个实现了该方法的对象,还有一次是真正的转发,是把这个函数的参数及相关信息打包成一个对象,把他发送给另外一个类,让他去处理(PS:消息重定向是返回一个提供了实现的对象,消息转发是将方法参数打包到一个对象里面,然后把这个对象发送出去)。
  1. 消息重定向:需要实现这个函数- (id)forwardingTargetForSelector:(SEL)aSelector,通过这个函数返回一个实现了该方法的对象。如果返回了self或者no的时候,就会进入消息转发,否则不会

定义一个新类,里面添加一个和student中未实现的方法同名的方法,而且这个方法有实现,假设叫testStudent,然后在student中实现下面的方法

//该方法返回一个添加了方法实现的对象,
-(id)forwardingTargetForSelector:(SEL)aSelector{
    if (aSelector == @selector(write)) {
    //studentTest类中这个方法有实现
        StudentTest *test = [[StudentTest alloc]init];
        return test;
    }
    return [super forwardingTargetForSelector:aSelector];
}
  1. 消息转发:需要实现两个方法

    methodSignatureForSelector:返回一个NSInvocation*的对象,将方法的参数返回值封装在里面。

    - (void)forwardInvocation:(NSInvocation *)anInvocation,将该对象发送给一个提供了方法实现的对象。

    和上面的消息重定向一样,只不过student中要实现的方法有了差别

//这个方法返回一个NSInvocation*的对象,里面打包了有关于这个未实现方法的信息
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    NSString*sel = NSStringFromSelector(aSelector);
    if ([sel isEqualToString:@"write"]) {
        return [NSMethodSignature signatureWithObjCTypes:"[email protected]:"];
    }
    return [super methodSignatureForSelector:aSelector];
}

-(void)forwardInvocation:(NSInvocation *)anInvocation{
    SEL selector = [anInvocation selector];
    StudentTest *test = [[StudentTest alloc]init];
    if ([test respondsToSelector:selector]) {
        [anInvocation invokeWithTarget:test];
    }
}

总结一下

发送一个消息:底部会转成objc_msgSend函数,这个函数除了方法的参数之外还有两个隐藏的参数self和_cmd,接下来就会按照下面的流程去调用这个函数

  1. 根据isa指针找到对象所属的类或者类所属的元类
  2. 先去类或者元类的cache列表中根据SEL去找这个方法。
  3. 没有找到,去method方法列表中找
  4. 还是没有找到,就去父类中找
  5. 找到了,根据SEL找到对应的IMP,调用这个函数
  6. 没有找到,进入动态方法解析或者消息转发。

动态方法解析:

runtime提供的第一次实现这个方法的机会,要实现resolveInstanceMethod/resolveClassMethod方法,给未实现的方法在运行时添加实现,返回no/不实现,进入消息转发。

消息转发一:消息重定向

消息重定向:返回一个实现了该方法的对象,要实现-(id)forwardingTargetForSelector:(SEL)aSelector函数,如果返会nil/self,则进入下面的消息转发二

消息转发二:消息转发

将未实现方法的相关信息打包成一个NSInvocation对象,然后交给一个类去实现。需要实现-(NSMethodSignature )methodSignatureForSelector:(SEL)aSelector和-(void)forwardInvocation:(NSInvocation )anInvocation方法

疑问?为什不直接找IMP而要通过SEL这个中间人呢?

SEL只是方法的编号,真是的实现是通过IMP来查找的,SEL和IMP之间是一一映射的关系,通过SEL我们可以改变他的IMP,然后让一个方法在不同的情况下有不同的实现,例如实现方法的交换,有时候我们需要给系统的方法添加一些自己的东西

1:可以通过一个子类继承于系统类,然后重写那个类的方法

2:通过分类,但是会覆盖系统的方法

3:写一个自己的方法,通过runtime在load方法中交换系统方法和自己的方法的实现

下面主要针对第三种例子举例:

第一步首先为我们要动手脚的系统方法类添加一个分类,

假设我们要为imageNamed添加一个判断nil的功能,先要为他添加一个分类,然后给系统的imageNamed方法添加前缀,明明一个自己的方法,如下

分类的h文件

分类的m文件,对于这里乍一看可能像递归,其实在第一次调用的系统的imageNamed方法时调用的是my_imageNamed方法,当第调用的my_imageNamed方法时其实在调用系统的imageNamed方法

交换

至于为什么要在load方法中写,我会在别的博客中提到。

时间: 2024-10-12 22:24:46

runtime总结二之消息机制(包括消息转发,消息交换的黑魔法)的相关文章

Android 手机卫士--解析json与消息机制发送不同类型消息

本文地址:http://www.cnblogs.com/wuyudong/p/5900800.html,转载请注明源地址. 1.解析json数据 解析json的代码很简单 JSONObject jsonObject = new JSONObject(json); //debug调试,解决问题 String versionName = jsonObject.getString("versionName"); mVersionDes = jsonObject.getString("

ios底层开发消息机制(四)消息转发

消息转发 若想令类能理解某条消息,我们必须以程序码实现出对应的方法才行.但是,在编译期向类发送了其无法解读的消息并不会报错,因为在运行期可以继续向类中添加方法,所以编译器在编译时还无法确知类中到底会不会有某个方法实现.当对象接收到无法解读的消息后,就会启动“消息转发”(message forwarding)机制,程序员可经由此过程告诉对象应该如何处理未知消息. 你可能早就遇到过经由消息转发流程所处理的消息了,只是未加留意.如果在控制台中看到下面这种提示信息,那就说明你曾向某个对象发送过一条其无法

从源码角度分析native层消息机制与java层消息机制的关联

上文从源码分析Handler机制中从java层分析了消息机制,接下来本文从native层去分析Android中的消息机制. 在一个消息驱动的系统中,最重要的就是消息队列和消息获取和处理,从上一篇文章可以看出handler的消息机制主要是靠MessageQueue进行消息列队,靠Looper进行消息循环,Looper的loop方法中进行轮询消息的实际操作还是依靠MessageQueue的next方法来获取消息,也就是说在这个消息驱动机制中最重要的就是MessageQueue这个类了.在Androi

8.windows消息机制(三)消息队列

1.消息队列 消息队列用于存放消息的一个队列,消息在队列中先入先出.所有窗口程序都具有消息队列,程序可以从队列中获取消息. 2.消息队列的类型 系统消息队列 - 由系统维护的消息队列,存放系统产生的消息,例如鼠标.键盘等. 程序消息队列 - 属于每一个应用程序(线程)的消息队列,由应用程序(线程)维护. 3.消息队列的关系 当鼠标.键盘产生消息时,会将消息存放到系统消息队列. 系统会根据存放的消息,找到对应窗口的消息队列.

NSObject头文件解析 / 消息机制 / Runtime解读 (一)

NSObject头文件解析 当我们需要自定义类都会创建一个NSObject子类, 比如: #import <Foundation/Foundation.h> @interface ClassA : NSObject @end 那么NSObject里面具体有什么呢? 我们点到它的头文件里面去看看 @interface NSObject <NSObject> { Class isa OBJC_ISA_AVAILABILITY; //每个NSObject对象都拥有一个Class类作为成员

Runtime的初步认识——消息机制

之前写过一篇<Runtime的初步认识>,读过的小伙伴们应该对OC中的类与C中的结构体的关系有了一定的了解.这篇文章就先介绍一下OC中的方法是如何"调用"的.这就是OC的另一个机制--消息机制. OC 的消息机制是指,在外部需要执行某个对象的方法时,使用的方式是"发送消息"而不是"调用". 在学 Runtime 之前你绝对不理解为什么是发送消息而不是调用."调用"时确定的,而最终要执行哪段代码是不确定的. 有可能现

Android消息机制Handler的实现原理解析

Android的主线程为什么可以一直存在? 线程是一个动态执行的过程,从产生到死亡包括五个状态:新建.就绪.运行.死亡和堵塞.只要线程没有执行完毕或者没有被其它线程杀死,线程就不会进入死亡状态.Android中的主线程一直存在是因为主线程中一直在监听消息,从而使线程无法被执行完毕. 线程的五种状态: 新建new Thread 当创建Thread类的一个实例对象时,此线程进入新建状态未被启动. 就绪runnable 线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等

windows消息机制(MFC)

windows消息机制(MFC) 消息分类与消息队列 Windows中,消息使用统一的结构体(MSG)来存放信息,其中message表明消息的具体的类型, 而wParam,lParam是其最灵活的两个变量,为不同的消息类型时,存放数据的含义也不一样. time表示产生消息的时间,pt表示产生消息时鼠标的位置. 按照类型,Windows将消息分为: (0) 消息ID范围 系统定义消息ID范围:[0x0000, 0x03ff]用户自定义的消息ID范围: WM_USER: 0x0400-0x7FFF 

深入解析Android中Handler消息机制

Android提供了Handler 和 Looper 来满足线程间的通信.Handler先进先出原则.Looper类用来管理特定线程内对象之间的消息交换(MessageExchange).Handler消息机制可以说是Android系统中最重要部分之一,所以,本篇博客我们就来深入解析Android中Handler消息机制. Handler的简单使用 为什么系统不允许子线程更新UI 因为的UI控件不是线程安全的. 如果在多线程中并发访问可能会导致UI控件处于不可预期的状态,那为什么不对UI控件的访

Android多线程编程之Handler篇(消息机制)

Android多线程编程之Handler篇(消息机制) Android的消息机制主要是指Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper的支撑. MessageQueue 消息队列,以队列的形式(实为单链表结构)对外提供插入和删除的工作, Looper 以无限循环的形式不断获取MessageQueue中的消息,有则处理,无则等待. ThreadLocal ThreadLocal可以在不同的线程互不干扰的存储并提供数据,通过ThreadLocal可以很