Method Swizzling与JSPatch探究(一)

38 篇文章 1 订阅
3 篇文章 0 订阅

以下笔记整理于2017-03-22
当时作为无埋点预研的学习整理,由于小组是客户端和前端混合的小组,所以下面尽可能以偏简单的文字在组内分享。
第二部分地址为:Method Swizzling与JSPatch探究(二)


  • 用最简单的文字,来简单说明Method Swizzling和JSPatch hook selector的原理。并试验当两者共用时,兼容情况。(无埋点预研)
  • 如果看此文之前,对这两者有些概念那就最好啦

    目录:
    1、从[receiver message]说起
    2、Method Swizzling hook过程
    3、JSPatch hook过程
    4、各种case兼容性
    5、结论
    6、拓展
    7、也许你有疑问的地方(主要针对3中可能产生的问题)
    
  • 写完之后表示:本来没想写这么长,越写越多,醉了。记录下来主要也是让自己查出一些知识漏洞

1、进入正文之前,从[receiver message]说起
OC 动态语言

执行函数的过程就是发送消息的过程,根据message生成selector。简化版流程图如下:

说明:

  • 发送消息,根据message生成selector
  • 根据selector寻找指向函数实现的指针IMP,存在直接执行
    不存在进入消息转发机制
  • resolveInstanceMethod动态方法解析-常用于给类/对象动态添加一个相应的实现
  • forwardingTargetForSelector备用接收者-将消息转发给其他对象处理
  • forwardInvocation完整转发-最后一次将消息转发给其他对象,包含一个表示消息的NSInvocation的对象,包含尚未处理的消息的全部细节,包括selectortargetparams
  • 以上都没完成转发,则doesNotRecognizeSelector,此时就会看到我们常见的unrecognize sent to selector balabala之类的错误
2、 Method Swizzling

method_exchangeImplementations为例

  • hook关键:未进入消息转发机制时,直接从IMP hook,交换2个方法的IMP

selORIG为原方法,IMPORIG为对应指向函数实现的指针

  • 原始方法

  • hook selORIG 一次

  • hook selORIG 两次,先A后B

    第二次hook,实质上已经跟selA无关,是selORIGselB对应的IMP的交换,因此selB对应IMPAselORIG对应IMPB。而selA不受影响仍然是IMPORIG

  • hook selORIG 三次,顺序为:A、B、C

    经过两次hook的解释,应该懂它的套路了

以上:不同的hook,是在不同的地方实现
先后顺序的保证:由于Method Swizzling都是写在+(void)load方法中的,compile Sources的顺序就是load方法的调用顺序(注意这里是已分类的形式的情况下,如果存在父类和子类同时swizzle,是先执行父类的+load,这也是一个坑-后续再讨论)

关于Method Swizzling如何实现,自行Google

3、JSPatch

相当普及的热修复库,虽然目前iOSer都收到苹果爸爸的警告邮件了,这个框架的未来走势还有待观察,但是智慧无穷,它总会存在。

一点说明

因为本文最后要讲Method SwizzlingJSPatch的兼容性。因此下文,不会涉及JS是如何和OC交互的?JSPatch很细节的实现是什么?只会涉及,它是如何hook,在哪一步hook

在哪一步&如何hook

白话文概括其原理:
1、 改变原指向函数实现的IMP指针为_objc_msgForward(或_objc_msgForwad_stret)
2、 执行方法,直接进入消息转发过程
3、 消息转发最后一步hook-forwardInvocation

下面用图来说明:

  • 搞事前:安然无恙



  • 搞事第一步:改变方法指向_objc_msgForward

    如上图:用过JSPatch都知道,我们可以通过self.ORIGtest()的方式调用原方法,是因为JSPatch新增了一个方法指向原来的方法
//提取几行关键的源码
IMP msgForwardIMP = _objc_msgForward;
....
class_replaceMethod(cls, selector, msgForwardIMP, typeDescription);


你可能会有疑问的地方,Answer见第7部分描述

    Q:万一,原方法不存在怎么办?  
    Q:`_objc_msgForward`是什么鬼?  



  • 搞事第二步:forwardInvocation
if (class_getMethodImplementation(cls, @selector(forwardInvocation:)) != (IMP)JPForwardInvocation) {
    IMP originalForwardImp = class_replaceMethod(cls, @selector(forwardInvocation:), (IMP)JPForwardInvocation, "v@:@");
    if (originalForwardImp) {
        class_addMethod(cls, @selector(ORIGforwardInvocation:), originalForwardImp, "v@:@");
    }
}



  • 搞事第三步:JPForwardInvocation里到底做了啥?
    经过搞事第一步我们知道,我们现在调用方法会直接进入消息转发机制的最后一步(以下是:包含关键步骤的不完整流程)。
    一句话:获得NSInvocation所有参数,将消息转发给JS对应实现,获得JS回调结果,完成调用

  • Step01:判断调用的方法该类是否已实现,未实现则走 原方法指向的消息转发流程(上图中即IMPforwardInvocation)
JSValue *jsFunc = getJSFunctionInObjectHierachy(slf, JPSelectorName);
if (!jsFunc) {
    JPExecuteORIGForwardInvocation(slf, selector, invocation);
    return;
}
  • Step02:处理调用函数的参数等相关信息
    这里invocation会被利用,关于invocation,前文已经提及
    这里涉及OC的type encoding(类型编码)

  • Step03:通过formatOCToJS将参数数组转为对应的JS对象数组

  • Step04:调用JS实现,传入之前处理的参数列表
    主要涉及的方法:- (JSValue *)callWithArguments:(NSArray *)arguments;


    你可能会有疑问的地方,Answer见第7部分描述

    Q:关于type encoding    
    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值