美文网首页
Runtime第五篇-方法和消息发送

Runtime第五篇-方法和消息发送

作者: lzh_coder | 来源:发表于2017-09-20 17:05 被阅读9次

先把Method的内存模型摆出来:

typedef struct objc_method *Method;

struct objc_method {

SEL method_name;

char* method_types;

IMP method_imp;

}

1,SEL-选择器

typedef struct objc_selector * SEL;

runtime并没有给出SEL的具体结构。实际上SEL是对方法名的一种映射。它出现的需求在于,方法的执行首先要找到方法Method结构体,在method的链表中,怎样去定位一个结构体。我们看Method的内存结构,一共三个字段。不用分析,只能通过method_name来定位到这个结构体,它是一个SEL,它是由方法名转化得来的。所以,SEL是用来定位Method的,进而执行Method的method_imp。

Objective-C的这种设计决定了Objective-C这门语言不支持重载。

SEL的相关操作:

const char * sel_getName ( SEL sel );

2,IMP-函数指针

id (*IMP)(id, SEL, ...)

3,Method的操作

3.1 执行一个方法

id method_invoke ( id receiver, Method m, ... );

3.2 获取方法名

SEL method_getName ( Method m );

3.3 获取Method的IMP

IMP method_getImplementation ( Method m );

3.4 获取Method的类型编码

const char * method_getTypeEncoding ( Method m );

3.5 获取方法返回值的类型字符串

char * method_copyReturnType ( Method m );

3.6 设置Method的imp

IMP method_setImplementation ( Method m, IMP imp );

3.7 交换Method的imp

void method_exchangeImplementations ( Method m1, Method m2 );

4,消息发送

Objective-C中的方法调用都是在runtime动态调用的,实际上是执行了objc_msgSend方法。

objc_msgSend(receiver, selector)

如果有参数:

objc_msgSend(receiver, selector, arg1, arg2, ...)

这个方法会动态查询方法的imp,class_getMethodImplementation,如果正常流程没有查询到,就会返回msg_forward的imp,就会走消息转发流程。

具体细节是这样的:

当消息发送给一个对象时,objc_msgSend通过对象的isa指针获取到类的结构体,首先会在类的结构体的cache里面查找selector,如果没有查找到,就去方法链表里面查找方法的selector。如果当前类没有找到selector,则通过类结构体中的superClass指针找到其父类,在父类中依旧是先查找cache,然后查找方法链表,如果找不到,会一直沿着类的继承体系到达NSObject类。一旦定位到selector,函数会就获取到了实现的入口点,并传入相应的参数来执行方法的具体实现。另外,在方法链表里面查询到的方法会被放到cache里面,提高selector的查找速度。如果最终还是找不到,就走消息转发流程。

5,消息转发

一般我们不确定一个实例对象是否响应某个方法的时候,我们会使用

- (BOOL)respondsToSelector:(SEL)aSelector;来确认一下,避免报unrecognized selector exception。

这里要讲的是,如果不调用respondsToSelector加保护,出现selector定位不到的时候怎么办,它的机制是怎么样的。

这个机制一共分三步,这一套机制NSObject里面提供了接口:

1,动态方法解析。--给没有实现的selector提供一种实现。

+ (BOOL)resolveInstanceMethod:(SEL)sel;//给实例selector提供一种实现

+ (BOOL)resolveClassMethod:(SEL)sel;//给类selector提供一种实现。

eg.

void functionForMethod1(id self, SEL _cmd) {

NSLog(@"%@, %p", self, _cmd);

}

+ (BOOL)resolveInstanceMethod:(SEL)sel {

     NSString *selectorString = NSStringFromSelector(sel);

     if ([selectorString isEqualToString:@"method1"]) {

            class_addMethod(self.class, @selector(method1), (IMP)functionForMethod1, "@:");

     }

    return [super resolveInstanceMethod:sel];

}

2,备用接受者。--更换消息接受者。

- (id)forwardingTargetForSelector:(SEL)aSelector; //提供一个新的消息接受者。

基于runtime的这个机制,我们可以做一些松耦合的东西。比如做一个中心类,client调用中心类的方法,中心类通过这一机制进行方法分发。

3,消息重新转发。--这一步不仅可以更改消息接受者,还可以更改消息的参数。

- (void)forwardInvocation:(NSInvocation *)anInvocation;

此外需要重写:

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;

eg.

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector

 {

        NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];

        if (!signature) {

                  if ([ARespondClass instancesRespondToSelector:aSelector]) {

                  signature = [ARespondClass instanceMethodSignatureForSelector:aSelector];

                  }

      }

      return signature;

}

- (void)forwardInvocation:(NSInvocation *)anInvocation 

{

       if ([ARespondClass instancesRespondToSelector:anInvocation.selector]) {

            [anInvocation invokeWithTarget:ARespondClassObject];

       }

}

在这个例子里面并没有修改NSInvocation,没有修改它的参数。在这一步里面,实际上可以做更加灵活的消息转发,必须追加一个消息参数等等。

组合和继承是面向对象的一个重要话题。runtime的消息转发机制提供了一种优雅的组合实现机制。对外,client调用中心类的方法,通过performSelector,对内,中心类对消息进行分发。

相关文章

  • Runtime第五篇-方法和消息发送

    先把Method的内存模型摆出来: typedef struct objc_method *Method; str...

  • Runtime-原理

    runtime初探对象与方法的本质runtime-消息发送runtime-动态方法解析runtime-消息转发 r...

  • 方法调用底层实现

    runtime怎么实现方法的调用 :消息机制,runtime系统会把方法调用转化为消息发送。即objc-msgSe...

  • runtime的消息机制

    任何方法调用本质:发送一个消息,用runtime发送消息,OC底层实现通过runtime实现; 我们平时书写的代码...

  • iOS 防止方法未实现造成的崩溃

    实现原理基于runtime的方法交换和消息发送机制 方法交换 method_exchangeImplementat...

  • runtime介绍

    前言:任何方法调用的本质:发送一个消息,用runtime来发送消息,OC底层实现通过runtime来实现。 run...

  • Runtime的底层实现

    任何方法调用的本质: 其实是发送了一个消息, 用runtime发送消息, OC底层实现通过runtime实现最终生...

  • RunTime

    1.使用消息发送机制创建对象,给对象发送消息 2. runTime方法交换的使用 3. KVO本质其实也是runtime

  • iOS runtime学习(二)

    iOS runtime学习(一) 1、发送消息 方法调用的本质,就是让对象发送消息。 objc_msgSend,只...

  • Runtime运行机制

    一、RunTime 简介 二、RunTime作用1.发送消息 SEL:方法编号,根据方法编号,就可以找到对应的方法...

网友评论

      本文标题:Runtime第五篇-方法和消息发送

      本文链接:https://www.haomeiwen.com/subject/ofuosxtx.html