iOS 远端代码下发,有什么设计思路 ?
一般大家都用 JSPatch?
用别人的,总不好。老王造了个轮子,我来描述一下。
老王 Patch, 挺先进的。JavaScript 代码, 采用 WebPack 打包。也参考了 JSPatch ,
怎么设计这个 Patch?
从原理上
Patch 主要是 干什么的呢?
一般大公司的 App 在运行的过程当中,业务线非常复杂,可能会出现一些问题。这个时候,打补丁比较好
可以远端下发一个文件,开发的 App 通过 加载 这个文件,
实行 这个 代码 补丁。这个样子,就可以通过远端, 把这个代码, 在开发的 app 运行当中,给执行过来,
OC 可以的,因为是动态语言,有 runtime ,所以才能搞这个 patch。
OC 有自己的消息转发流程。 Runtime 有 objc_msgSend
和 _objc_msgForward
。
这样就可以干一些 patch 的事情。
另外一点, 在 App 中植入了这个 patch ,他所使用的语言,能够被 eval 。
eval, 判断代码语句可以执行
这样 app 中有一个 context, 可以执行用于 patch 的语言。
本文中的 iOS 补丁方案直接运用 这个 JavaScriptCore,
就是使用 JavaScriptCore 提供的 JSContext.能让 JavaScript 语言 和 Objective-C 之间,有一个接口。然后就可以 JavaScript 与 Objective-C 相互调用了。( 苹果自己实现的 )JSPatch 用到了 FFI,这个库老王 Patch 也使用了 FFI .
FFI, Foreign Function Interface ,就是把一个语言暴露出来的接口,能让其他语言来调用。
Java 的 JNI 标准,与 FFI 比较相似。FFI ,首先遵从 Coding Convention ,就是定义遵守一些调用的协议和规定。为什么要有 FFI 呢?举个 ?:
我们执行一个函数调用,首先开辟一个栈帧,这个栈帧传递一些什么样的参数?参数的类型是什么?参数 的个数,是多少?包括这个函数里面,执行的一些动作是什么?
FFI 就把遵守的一些调用的协议和规定定义出来。老王 Patch 库的这个自定义 FFI , 就是把想做的 patch 语言, 对接 Objective-C 的执行环境。
需要往里面传递的命令,把这个东西,给规定出来。这个样子, 在执行这个 Patch 的过程中,就可以按照这个命令,去发相应的消息,让相应的 context ,执行需要 patch 的代码。
FFI 就到这里了。 开启预编译。
使用 JSPatch 的过程当中,老王发现,用的非常的不顺手,
为什么他不顺手呢?老王以为,JSPatch 把 JavaScript 硬生转换成相当于 Objective-C 这种风格的,补丁代码。其间,还做了很多的语言处理,包括 Bang 说的源函数的处理。包括 C 语言函数,类似 Ruby 的 Method Mission .至于其他, 老王以为, JSPatch 都是在后端进行的。JavaScript 的 逼真度
要加强Patch, 本身具有的语言的一个平滑性,就是写 JavaScript , 就使用 JavaScript 的写法
引出了预编译的过程:
把 JavaScriptContext 的注册接口,抽象出三个层,
define, 定义
define, 往 Objective-C 发消息。 不需要参数返回, 可以用 define .
需要定义的,肯定都自己定义的。抽象出这一层,就可以了。evaluate
需要一个返回值的时候,使用
去执行一些什么任务Callback
老王感觉苹果封装的 JavaScriptContext,可能有一些问题。
比如说, 传递的一些函数对象,开发者封装了两层。那就可能获取不到这个对象了。这个时候, 需要 Callback 方式,在那个执行环境当中,获取前端的 function 内容。用 JavaScriptCore 相关的一种回调方式,来取到在当前 JavaScriptContext 环境 当中的一个 function 值。还有就是指令类型
这个 patch ,是干什么的? 对所使用的内容, 有哪些指令?
- Patch 就是 执行 一些 Method 函数 的 一些 hook,
- 会修改 一些 property 的值了。
- 会 做一些 block 方面的改动了。
- 访问 父类 super
- 我们 可以 新增 函数,method_create
通过 这些指令,我们可以实现, 想要 patch 到的一些功能
还有关键字转换:这个通过预处理来实现。要完成对一些关键字的处理。
对于 Objective-C 中的 self 关键字, 通常用来代表当前对象的指针。
还需要改 super 关键字, 为 oc_super. 因为 super 在 ECMAScript 6 里面, 也是关键字了,需要回避掉。original, 是 Patch 中特有的。调用之前的函数,即打补丁之前的这个函数的调用。一些优化,举个 ?: JS 写 高阶函数
对 JavaScript 高阶函数做一个平滑处理,否则可能写起来,非常费劲。
a.request( function(a:id,b:Int):double{},callback:(string,string) => int,(num1:string, num2:double) => { return num1 + num2; } );
request 方法, 有三个参数。三个 function 类型的参数。第一个 function 函数,接收两个参数。第二个参数 callback, 是我们在函数调用上下文中取到的。第三个参数是, ECMAScript 6 中使用的箭头函数。
最好呢,对这个进行一些支持。这些操作,是通过预处理的方式,编译成,能够对接 OC 的 Block 指令的。然后,才能 正常 完成工作。
JSPatch 是很好的参考,
大家都仿照 JSPatch,在 JavaScriptContext 中注册一大堆自己要进行 patch 的函数。
想要用到什么,就去补充什么。一般都是完全借鉴 Bang 的一些想法。最好呢? 要把 JSPatch 的这么一些想法,背后的东西,给抽象出来。
缺点,没使用挺好用的 JSExport 协议, JSVirtualMachine
Github 官方 repo 链接:
我的民间 repo ,