steipete/Aspects
的设计,挺高深莫测的
- 一般我们 hook, hook 一个类的所有实例的方法
这里 hook 的粒度,到了对象级别
- 而且不需要建立,大量的 IMP
利用到了固有的方法转发的流程
本文主要参考 woshiccm/Aspect
分析下 Aspects 的 swift 源代码实现
Aspects 的大概设计
1, 方法换一个时机执行
1.1, 方法转发的流程
方法执行的时候,将 hook 的 selector
的实现 IMP 替换为 _objc_msgForward
或者_objc_msgForward_stret
,
此时调用 selector
, 就会进行消息转发
1.2,实际做事情的函数
将 forwardInvocation
的实现, 替换为自定义的函数
__ASPECTS_ARE_BEING_CALLED__
并添加 __aspects_forwardInvocation
的实现为 forwardInvocation
原来的实现。
我们 hook 的方法 selector
,需要进行消息转发
我们 hook 的方法 selector
, 都会执行自定义的函数
__ASPECTS_ARE_BEING_CALLED__
1.3, 自定义的函数中
执行了原来的方法,和 hook 的 block
这样,原来的方法,还是跑了,但是在一个不同的时机
方便我们处理
2, 需要执行的 hook 方法
得到了有利的时机
把 hook 在前面,在后面,用来替换的方法,给执行了
2.1, 执行对应的 block
Aspects 中,把 hook 的 block 的描述信息中,捞出 NSMethodSignature
使用 NSMethodSignature,执行 hook 的 block
2.2,执行原来的方法
NSInvocation 的实例, invoke
下
Swift Aspect 中的具体实现
3.1 调用
有这么个实例方法
@objc dynamic func test(id: Int, name: String) {
print("come on")
}
hook 上面那个方法
_ = try? hook(selector: #selector(ViewController.test(id:name:)), strategy: .before) { (_, id: Int, name: String) in
print("done")
}
开始 hook
@discardableResult
func hook<Arg1, Arg2>(
selector: Selector,
strategy: AspectStrategy = .before,
block: @escaping(AspectInfo, Arg1, Arg2) -> Void) throws -> AspectToken
{
// 拿到需要插入执行的 block
let wrappedBlock: @convention(block) (AspectInfo) -> Void = { aspectInfo in
guard aspectInfo.arguments.count == 2,
let arg1 = aspectInfo.arguments[0] as? Arg1,
let arg2 = aspectInfo.arguments[1] as? Arg2 else { return }
block(aspectInfo