博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
04. WebApp2.0时代启程:跨平台的JSPatch
阅读量:6882 次
发布时间:2019-06-27

本文共 3763 字,大约阅读时间需要 12 分钟。

紧接上回,Cocos2d-JS通过JSBinding从C++API到JSAPI,完成了H5的跨平台加速,这一回,我们一起来见证一下JSPatch的跨平台实现,为JS语言增加消息转发机制,无需修改js脚本,让下面这段代码可以正确地运行起来:

var controller = UIViewController.alloc().init();

而不是让app翻译成:

UIViewController.__c('alloc')().__c('init')();

JSPatch依赖JavaScriptCore作为运行环境,在iOS7.0之后越来越受到终端开发者的欢迎,我们简单地分析JSPatch的技术框架和原理,来引出SpiderMonkey在Patch上的lazy加载机制:

1. JSPatch通过JS动态调用OC的代码

使用反射机制

`

Class class = NSClassFromString("UIViewController");    id viewController = [[class alloc] init];    SEL selector = NSSelectorFromString("viewDidLoad");    [viewController performSelector:selector];

`

使用runtime机制动态注册类,并添加函数
`

Class superCls = NSClassFromString(superClassName);    cls = objc_allocateClassPair(superCls, className.UTF8String, 0);    objc_registerClassPair(cls);    class_addMethod(cls, @selector(ORIGforwardInvocation:), originalForwardImp, "v@:@");

`

替换掉掉原有的实现,增加JS拦截
`

IMP originalImp = class_respondsToSelector(cls, selector) ? class_getMethodImplementation(cls, selector) : NULL;IMP msgForwardIMP = _objc_msgForward;class_replaceMethod(cls, selector, msgForwardIMP, typeDescription);

`

通过require增加全局变量
`

var _require = function(clsName) {    if (!global[clsName]) {        global[clsName] = {        __isCls: 1,        __clsName: clsName;        };    }    return global[clsName]}

`

修改JS脚本,通过正则替换JS的表达式:

UIView.alloc().init();替换后UIView.__c('alloc')().__c('init')();

2. 因为JS语言没有转发机制,无法用懒加载机制,来动态的改版语言的调用,因此,JSPatch绕了很大一个弯,使用runtime替换了Objective-C的函数,动态注册新的类和函数,新增加了一些JSAPI,尽可能的让JS的写法更贴近Native方式,降低Native开发者对JSPatch的门槛,然而并没有像wax框架一样,让脚本的写法更自然。

问题的核心是,JS能否拥有API的转发机制,通过运行时动态加载和调用,打开JSClass,查看描述信息:

screenshot

查看JSResolveOp方法的说明:

`
解决在一个Obj对象上的懒加载属性,这些属性按照需求映射在Native对象;
JS会首先在Obj上查找该属性,如果没有找到,则调用该方法解析该属性;
如果解析成功,JS引擎将再次调用Obj.ID,如果没有找到,会提交给它的父类解析;
JSNewResolveOp提供了更便宜的方式来解决延迟属性;
`

3. 我们看一下我们将采用的技术架构:

screenshot

  1. 当SpiderMonkey遇到UIVIew的时候,会触发全局global对象的属性查找,如果没有找到,交给JSResolveOp方法处理,这里我们重载的JSResolveOp方法:
bool AJContext::Resolve(JSContext *cx, HandleObject handle, HandleId handId, MutableHandleObject retval) {    char* name = JS_EncodeStringToUTF8(cx, RootedString(cx, JSID_TO_STRING(handId)));    Class clazz = objc_getClass(name);    if (clazz) {        AJContext*   nactx = (AJContext*)JS_GetContextPrivate(cx);        RootedObject proxy(cx, JS_NewObject(cx, &AJProxy::Clazz, RootedObject(cx, nactx->prototypeProxy()), NullPtr()));        JS_SetPrivate(proxy, new AJProxy(false, clazz));        JS_DefinePropertyById(cx, handle, handId, proxy, 0);        retval.set(handle.get());    }    return true;}
  1. 这里我们只是使用JSObject的空的代理,我们将所有数据保存在一个Native的对象中:
class AJProxy {public:    void* prt;    bool isObject;    char* classNamepublic:    GetterAndSetterMethods....}
  1. 调用对象的init方法:
bool AJProxy::NewResolve(JSContext *cx, JS::HandleObject handle, JS::HandleId handId, JS::MutableHandleObject retval) {    const char* name = JS_EncodeStringToUTF8(cx, RootedString(cx, JSID_TO_STRING(handId)));    fprintf(stderr, "%s\n", name);    AJProxy* proxy = (AJProxy*)JS_GetPrivate(handle);    if (proxy->isObject()) {        //...处理对象方法    }    else {        // ...处理类方法        id clazz  = (id)proxy->target();        id object = objc_msgSend(clazz, sel_registerName(name));        AJContext*   nactx = (AJContext*)JS_GetContextPrivate(cx);        RootedObject proxy(cx, JS_NewObject(cx, &AJProxy::Clazz, RootedObject(cx, nactx->prototypeProxy()), NullPtr()));        JS_SetPrivate(proxy, new AJProxy(true, object));        JS_DefineFunction(cx, handle, name, NewFunction, 0, 0);        retval.set(handle.get());    }    return true;}

4. 至此,一个跨平台的JSPatch的Demo写完了。

需要补充的地方也很多,比如继承、类型转换等,作者很喜欢SpiderMonkey、C++,这种兼容iOS和Android的事情,还需要很多很多的工作。

这里我们主要讲得时SpiderMonkey,通过这个小巧的JsVM,我们可以重新改写很多JS的不擅长的地方,毕竟js的开发者要比lua、wax、swift多很多。
相信不需要多久,就会有人写出跨平台的JSPatch,使用JS开发的同学会越来越多。

(总结)想要了解的更多,就要深入底层,下一站,我们带来OpenGL ES2.0 与JS的混合开发。

转载地址:http://okbbl.baihongyu.com/

你可能感兴趣的文章
ceph故障:too many PGs per OSD
查看>>
android事件分发(二)
查看>>
自定义 alert 弹窗
查看>>
Chrome浏览器扩展开发系列之三:Google Chrome浏览器扩展的架构
查看>>
oracle-rman-3
查看>>
Pycharm快速复制当前行到下一行Ctrl+D
查看>>
在QT中使用同步阻塞式HTTP请求的方法
查看>>
为了安全,linux下如何使用某个用户启动某个进程?
查看>>
【R】函数-其它实用函数
查看>>
scala分析数据作图
查看>>
This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its de 错误解决办法
查看>>
Linux常用基本命令(less)
查看>>
PostgreSQL各命令行工具功能说明
查看>>
关于后台接收参数为null的问题之ajax--contentType
查看>>
基于IDEA的JavaWeb开发环境搭建
查看>>
JAVA中如何将一个json形式的字符串转为json对象或对象列表
查看>>
Dynamics4.0和Dynamics2011处理Email的方法
查看>>
LeetCode: Regular Expression Matching 解题报告
查看>>
表数据文件DBF的读取和写入操作
查看>>
前后端分离趋势谈
查看>>