消息转发机制与Aspects源码解析

前言

最近在搞重构相关的事情,遇到了不少这样的场景:

进入一个界面,在viewWillAppear:的时候做相应判断,如果满足条件则执行对应代码。

这类业务有一个特点,业务内容是对应整个App的,与对应的ViewController毛关系都没有,但是却不得不耦合到(即使是调用代码可以精简到一行)ViewController中。

我们都知道,这种类似的业务用AOP(面向切片编程)来做十分适合,所谓面向切片编程就是在不修改原方法的前提下,动态的插入自己的想要的执行代码,由于Objective C是动态语言,可以很容易的利用method swizzling来实现AOP。

在正文之前特别感谢微信阅读团队的这篇博客:

这篇博客原理上讲解的比较清楚,但是细节上并没有讲的很详细,所以也就有了本文。


Objective C方法调用过程

这个其实我之前在这篇博客里讲过:

这里,把核心的内容再一次列出来。

如下Objective C代码

- (NSInteger )myTestFunction:(NSInteger)input{
    return input + 1;
}

- (void)mySpecialFunction{
   NSInteger result =  [self myTestFunction:10];
}

用clang来重写为C++,

clang -rewrite-objc  MyClass.m

然后,我们通过搜索mySpecialFunction方法名字,来找到转换后的代码,经过简单整理如下

static NSInteger _I_MyClass_myTestFunction_(MyClass * self, SEL _cmd, NSInteger input) {
    return input + 1;
}
static void _I_MyClass_mySpecialFunction(MyClass * self, SEL _cmd) {
   NSInteger result = objc_msgSend(self, sel_registerName("myTestFunction:"),10);
}

我们看到,方法体进行了如下转换

//OC
- (NSInteger )myTestFunction:(NSInteger)input{
    return input + 1;
}
//C++
static NSInteger _I_MyClass_myTestFunction_(MyClass * self, SEL _cmd, NSInteger input) {
    return input + 1;
}

方法调用进行了如下转换

//OC
NSInteger result =  [self myTestFunction:10];
//C++
NSInteger result = objc_msgSend(self, sel_registerName("myTestFunction:"),10);

不难看出,方法的调用并不是直接转换成了对应的C/C++方法调用,而是调用了objc_msgSend通过SEL(就是一个字符串)在运行时动态找到这个的执行体_I_MyClass_myTestFunction_

那么,在运行时如何找到这个方法的执行体呢? 这里省略一些细节,对细节感兴趣的同学可以看我上文写的那篇文章。一个实例方法的流程如下:

  • 对象实例收到消息(SEL+参数)
  • 根据存储在对象实例中的ISA到类对象,类对象依次查找Class Cache(方法表缓存)和dispatch table找到对应的Method,如果找到Method,执行对应Method的IMP(方法体),并且返回结果
  • 如果找不到Method,则根据类对象中的super_class指针找到父类的Class对象。一直找到NSObject的类对象
  • 如果NSObject也无法找到这个SEL,则进入消息转发机制
  • 如果消息转发机制无法处理,则抛出异常: doesNotRecognizeSelector

Method Swizzling

通过上文我们知道,一个方法的调用实际上就是SEL(方法名)通过Runtime找到IMP(方法执行体)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值