iOS 远端代码下发: 使用 JS 设计 Patch

iOS 远端代码下发,有什么设计思路 ?

通常你们都用 JSPatch?前端

用别人的,总很差。老王造了个轮子,我来描述一下。git

老王 Patch, 挺先进的。JavaScript 代码, 采用 WebPack 打包。也参考了 JSPatch ,

怎么设计这个 Patch?github

从原理上


Patch 主要是 干什么的呢? 通常大公司的 App 在运行的过程中,业务线很是复杂,可能会出现一些问题。后端

这个时候,打补丁比较好

能够远端下发一个文件,开发的 App 经过 加载 这个文件, 实行 这个 代码 补丁。bash

这个样子,就能够经过远端, 把这个代码, 在开发的 app 运行当中,给执行过来,

OC 能够的,由于是动态语言,有 runtime ,因此才能搞这个 patch。app

OC 有本身的消息转发流程。 Runtime 有 objc_msgSend_objc_msgForward 。 Runtime 有这两个方法, 这两个函数特性,让全部的函数调用都会走这两个方法。函数

这样就能够干一些 patch 的事情。

另一点, 在 App 中植入了这个 patch ,他所使用的语言,可以被 eval 。

eval, 判断代码语句能够执行

这样 app 中有一个 context, 能够执行用于 patch 的语言。优化


本文中的 iOS 补丁方案直接运用 这个 JavaScriptCore, 就是使用 JavaScriptCore 提供的 JSContext. 能让 JavaScript 语言 和 Objective-C 之间,有一个接口。而后就能够 JavaScript 与 Objective-C 相互调用了。 ( JavaScriptCore, 固然是苹果本身实现的 )ui

JSPatch 用到了 FFI,这个库老王 Patch 也使用了 FFI .

FFI, Foreign Function Interface ,就是把一个语言暴露出来的接口,能让其余语言来调用。 Java 的 JNI 标准,与 FFI 比较类似。 FFI ,首先听从 Coding Convention ,就是定义遵照一些调用的协议和规定。 为何要有 FFI 呢?lua

举个 🌰:

咱们执行一个函数调用,首先开辟一个栈帧,这个栈帧传递一些什么样的参数?参数的类型是什么?参数 的个数,是多少?包括这个函数里面,执行的一些动做是什么? 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 的这么一些想法,背后的东西,给抽象出来。

老王 Patch 的缺点,没使用挺好用的 JSExport 协议, JSVirtualMachine

Github 官方 repo 连接:https://github.com/wangyunico/iOSPatchBackend

个人民间 repo , https://github.com/BoxDengJZ/lao_wang_patch

相关文章
相关标签/搜索