iOS热修复方案 系列二 、Aspects如何用来热更新

iOS热修复方案 系列二 、Aspects如何用来热更新继续上篇文章,iOS开发中热修复很重要,这篇讲如何用Aspects进行热修复。Native层只要透出两种能力就基本可以了:在任意方法前后注入代码

大家好,欢迎来到IT知识分享网。

继续上篇文章,iOS开发中热修复很重要,这篇讲如何用Aspects进行热修复。

一、Aspects为什么可以热更新

要达到修复,Native 层只要透出两种能力就基本可以了:

  1. 在任意方法前后注入代码、替换代码 的能力。
  2. 调用任意类/实例方法的能力。

第 2 点不难,只要把 [NSObject performSelector:…] 那一套通过 JSContext 暴露出来即可。难的是第 1 点。而Aspects是可以满足的,只要把它的几个方法通过 JSContext 暴露给 JS 就可以了。

Aspects 是可以通过 AppStore 的审核。

[UIViewController aspect_hookSelector:@selector(viewWillAppear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo, BOOL animated) { NSLog(@"View Controller %@ will appear animated: %tu", aspectInfo.instance, animated); } error:NULL]; 

这篇文章参考了limboy的文章,现在网上的热更新也基本都是在他的基础上改来改去,我们这次讲的是limboy开源的代码,github地址:https://github.com/lzyy/felix。

下面我们写段崩溃的代码:

iOS热修复方案 系列二 、Aspects如何用来热更新

崩溃

然后我们修复一下:

iOS热修复方案 系列二 、Aspects如何用来热更新

热更新修复崩溃问题

如果想修改一个ViewController里面的UItableView的代理方法(例如tableView: numberOfRowsInSection:),上面的字符串替换成:

fixInstanceMethodReplace("MyTableViewController", "tableView:numberOfRowsInSection:", function(instance, invocation){ // 这里就是新的实现 }) 

热更新过程:

首先通过网络请求再结合一些加密 获取下发的js 字符串。然后执行[Felix evalString:js字符串]方法 就可以了。

为了读源代码,我们先来温习一下JavaScriptCore。

如果对这块比较熟悉的话就可以跳过这一小节。

JavaScriptCore

JavaScriptCore是webkit的一个重要组成部分,主要是对JS进行解析和提供执行环境。

我们可以脱离webview直接运行我们的js。iOS7以前我们对JS的操作只有webview里面一个函数 stringByEvaluatingJavaScriptFromString,JS对OC的回调都是基于URL的拦截进行的操作。

JSContext是JS执行的环境。一个 Context 就是一个 JavaScript 代码执行的环境,也叫作用域。

JSValue:我们对JS的操作都是通过它。每个JSValue都是强引用一个context。OC和JS对象之间的转换也是通过它。

OC和JS之间的通信

1、OC中执行JS:

 self.context = [[JSContext alloc] init]; NSString *js = @"function add(a,b) {return a+b}"; [self.context evaluateScript:js]; JSValue *n = [self.context[@"add"] callWithArguments:@[@2, @3]]; NSLog(@"---%@", @([n toInt32]));//---5 

2、JS调用OC

 self.context = [[JSContext alloc] init]; self.context[@"add"] = ^(NSInteger a, NSInteger b) { NSLog(@"---%@", @(a + b)); }; [self.context evaluateScript:@"add(2,3)"]; 

我们定义一个block,然后保存到context里面,其实就是转换成了JS的function。然后我们直接执行这个function,调用的就是我们的block里面的内容了。

实际中的简单例子:

OC调用JS的nativeCallJS方法。

JS调用OC的jsCallNative方法。

<html>
<body>
 <script type="text/javascript">
 var nativeCallJS = function(parameter) {
 alert (parameter);
 };
 </script>
 <button type="button" onclick = "jsCallNative('jsParameter')"/>调用OC代码</button>
</body>
</html>

OC中的代码:

- (void)doSomeJsThings{ self.jsContext = [_webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception) { NSLog(@"出现异常,异常信息:%@",exception); }; //oc调用js JSValue * nativeCallJS = self.jsContext[@"nativeCallJS"]; [nativeCallJS callWithArguments:@[@"hello word"]];//调用了js中方法"nativeCallJS",并且传参数@"hello word" //在本地生成js方法,供js调用 self.jsContext[@"jsCallNative"] = ^(NSString *paramer){ JSValue *currentThis = [JSContext currentThis]; JSValue *currentCallee = [JSContext currentCallee]; NSArray *currentParamers = [JSContext currentArguments]; dispatch_async(dispatch_get_main_queue(), ^{ // js调起OC代码,代码在子线程,更新OC中的UI,需要回到主线程 NSLog(@"js传过来:%@",paramer); }); NSLog(@"JS paramer is %@",paramer); NSLog(@"currentThis is %@",[currentThis toString]); NSLog(@"currentCallee is %@",[currentCallee toString]); NSLog(@"currentParamers is %@",currentParamers); };//生成native的js方法,方法名:@"jsCallNative",js可直接调用此方法 } 

三、分析felix原理

felix的github地址已经在上面给出了。

我们看上面热更新修复的代码,第一句是:

[Felix fixIt]; 

下面我们看下这个方法:

iOS热修复方案 系列二 、Aspects如何用来热更新

第一句:

 JSContext *tempContext = [self context]; 

先初始化了一个JSContext单例。

iOS热修复方案 系列二 、Aspects如何用来热更新

然后执行:

tempContext[@"fixInstanceMethod"] = ^(NSString *instanceName, NSString *selectorName, JSValue *fixImpl) { [self _fixWithMethod:NO aspectionOptions:AspectPositionInstead instanceName:instanceName selectorName:selectorName fixImpl:fixImpl]; }; 

调用的fixInstanceMethod方法:

iOS热修复方案 系列二 、Aspects如何用来热更新

代码里,先根据传过来的instanceName 实例化一个对象(或者类)。然后根据传过来的方法名 初始化SEL,然后调用Aspects的aspect_hookSelector方法。传入的是AspectPositionInstead,表示替换之前的方法。

然后在usingBlock里回调方法:

[fixImpl callWithArguments:@[aspectInfo.instance, aspectInfo.originalInvocation, aspectInfo.arguments]]; 

回到刚开始我们解决崩溃问题的代码:

iOS热修复方案 系列二 、Aspects如何用来热更新

fixImpl对应的是:

function(instance, originInvocation, originArguments){ if (originArguments[0] == 0) { console.log('zero goes here'); } else { runInvocation(originInvocation); } }); 

这样就解决了问题。

上图中,最后执行JavaScriptCore的evaluateScript方法:

[Felix evalString:fixJsStr]; 里面具体的实现: + (void)evalString:(NSString *)javascriptString { [[self context] evaluateScript:javascriptString]; } 
iOS热修复方案 系列二 、Aspects如何用来热更新

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/84287.html

(0)

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

关注微信