关于在iOS中AOP思想与组件化相结合的架构模式探索(AOP实现热更新)(二)


关于在iOS中AOP思想与组件化相结合的架构模式探索(AOP实现热更新)(二)

既然上一篇讲到了利用AOP思想可以动态的添加功能,那我们这篇就讨论下可否实现理论上的热更新。

我们同样利用Aspects去实现

首先我们可以查看Aspects源码

1
2
3
4
5
6
7
typedef NS_OPTIONS(NSUInteger, AspectOptions) {
AspectPositionAfter = 0, /// 在原始实现之后调用 (default)
AspectPositionInstead = 1, /// 将替换原来的实现。
AspectPositionBefore = 2, /// 在原始实现之前调用。

AspectOptionAutomaticRemoval = 1 << 3 /// 将在第一次执行之后删除hook。
};

看了这个位移枚举的值,是不是眼前一亮,我们完全可以利用Aspects去hook相关的方法然后在插入我们自己的代码,如果我们自己的代码是通过动态下发的,那不就可以实现热更新了吗?

一、首先实现插入代码的方法

上一篇,我们利用Aspects中提供的+ (id)aspect_hookSelector(SEL)selector withOptions:(AspectOptions)options usingBlock:(id)block error:(NSError **)error;去实现方法交换,分析这个方法:

1
2
3
4
5
6
7
8
//类名去掉用此方法
//参数1 selector 使我们要hook的方法名
//参数2 options 即上面分析的位移枚举
//参数3 block返回AspectInfo协议对象
+ (id<AspectToken>)aspect_hookSelector:(SEL)selector
withOptions:(AspectOptions)options
usingBlock:(id)block
error:(NSError **)error;

那么我们需要传递这些参数去给Aspects进行hook:

1
2
3
4
5
6
7
8
9
10
+ (void)hookWithClassName:(NSString *)className selector:(NSString *)selector options:(AspectOptions)options{
Class cla = NSClassFromString(className);
SEL sel = NSSelectorFromString(selector);
[cla aspect_hookSelector:sel
withOptions:options
usingBlock:^(id<AspectInfo>aspectInfo){
NSLog(@"我是Hook方法");
}
error:nil];
}

然后我们在相应的位置调用此方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self.view addSubview:self.btn];
[self.btn addTarget:self action:@selector(btnClick) forControlEvents:(UIControlEventTouchUpInside)];
//调用hook方法 传递的options为方法前调用
[AspectsHook hookWithClassName:@"ViewController" selector:@"btnClick" options:AspectPositionBefore];

}
- (void)btnClick{
NSLog(@"正常点击");
}
- (UIButton *)btn{
if (!_btn) {
_btn = [UIButton buttonWithType:UIButtonTypeCustom];
_btn.frame = CGRectMake(100, 100, 100, 100);
[_btn setTitle:@"点击" forState:(UIControlStateNormal)];
[_btn setBackgroundColor:[UIColor blueColor]];
}
return _btn;
}

@end

我们此时点击按钮看到的打印信息:

1
2
2018-09-06 18:00:45.805674+0800 Demo1Method[24569:1844918] 我是Hook方法
2018-09-06 18:00:45.805879+0800 Demo1Method[24569:1844918] 正常点击

那么,怎么实现热更新啊?

二、这里我们就需要熟练的掌握JSContext

这里要使用到的JavaScriptCore是iOS7之后新推出的API,JSContent要怎么使用啊?

JSContext : JSContext是一个JavaScript执行环境。所有
JavaScript执行发生在一个上下文中,所有JavaScript值
与上下文相关。

JSValue : JSValue是对JavaScript值的引用。每个JSValue
源自JSContext并持有对它的强引用。
当JSValue实例方法创建新JSValue时,新值
源自相同的JSContext。

具体使用方法我们在这里不展开讨论,这里贴上搜索链接:https://www.aliyun.com/jiaocheng/topic_38345.html

具体怎么用呢?我们先假设一段JS代码:

1
2
3
//具体我们hook了ViewController的btnClick方法,
//传入的2指的是位移枚举的AspectPositionBefore,
hookMethod("ViewController","btnClick",2);

首先注册JSContext方法:

1
2
3
4
JSContext * content = [[JSContext alloc] init];
content[@"hookMethod"] = ^(NSString *className, NSString *selectorName, AspectOptions options){
[self hookWithClassName:className selector:selectorName options:options];
};

我们通过下面方法获取JSValue,然后转化为object类型:

1
2
JSValue *jsValue = [[self context] evaluateScript:jsString];
id obj = jsValue.toObject;

最后我们去实现Ta:

1
2
3
4
[AspectsHook hook];
NSString *jsPath = [[NSBundle mainBundle] pathForResource:@"hookMethod" ofType:@"js"];
NSString *jsString = [NSString stringWithContentsOfFile:jsPath encoding:NSUTF8StringEncoding error:nil];
[AspectsHook getJSString:jsString];

然后运行点击按钮得到的事件:

1
2
2018-09-06 23:05:59.003088+0800 Demo1Method[26189:2045481] 我是Hook方法
2018-09-06 23:05:59.003253+0800 Demo1Method[26189:2045481] 正常点击

但此时我们的“我是Hook方法”还是写死到本地代码中的,那么我们怎么去执行js下发的实现代码呢?
通过JSValue的callWithArguments再次调起JSContext的响应方法。

1
2
3
4
5
6
7
8
9
10
11
+ (void)hookWithClassName:(NSString *)className selector:(NSString *)selectorName options:(AspectOptions)options value:(JSValue *)value{
Class cla = NSClassFromString(className);
SEL sel = NSSelectorFromString(selectorName);
[cla aspect_hookSelector:sel
withOptions:options
usingBlock:^(id<AspectInfo>aspectInfo){
NSLog(@"我是Hook方法");
[value callWithArguments:@[aspectInfo.instance,aspectInfo.originalInvocation,aspectInfo.arguments]];
}
error:nil];
}

那么我们引入代码的话,就得涉及到NSInvocation的使用,我们下节再讨论。


文章作者: Flonger
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Flonger !
 上一篇
关于在iOS中AOP思想与组件化相结合的架构模式探索(AOP实现热更新)(三)NSInvocation的使用 关于在iOS中AOP思想与组件化相结合的架构模式探索(AOP实现热更新)(三)NSInvocation的使用
关于在iOS中AOP思想与组件化相结合的架构模式探索(AOP实现热更新)(三)NSInvocation的使用 NSInvocation是调用函数的另一种方式,它将调用者,函数名,参数封装到一个对象,然后通过一个invoke函数来执行被调用的
2018-02-03
下一篇 
关于在iOS中AOP思想与组件化相结合的架构模式探索(AOP实现热更新)(一) 关于在iOS中AOP思想与组件化相结合的架构模式探索(AOP实现热更新)(一)
关于在iOS中AOP思想与组件化相结合的架构模式探索(AOP实现热更新)(一) 来自百度: AOP(Aspect Oriented Programming)面向切面编程,可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态
2018-01-31
  目录