04. WebApp2.0时代启程:跨平台的JSPatch-阿里云开发者社区

开发者社区> 淘朗英> 正文

04. WebApp2.0时代启程:跨平台的JSPatch

简介: 紧接上回,Cocos2d-JS通过JSBinding从C++API到JSAPI,完成了H5的跨平台加速,这一回,我们一起来见证一下JSPatch的跨平台实现,为JS语言增加消息转发机制,无需修改js脚本,让下面这段代码可以正确地运行起来: var controller = UIViewController.alloc().init(); 而不是让app翻译成: U
+关注继续查看

紧接上回,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* className
public:
    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的混合开发。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
4068 0
[更新]跨平台物联网通讯框架 ServerSuperIO v1.2(SSIO),增加数据分发控制模式
1.【开源】C#跨平台物联网通讯框架ServerSuperIO(SSIO) 2.应用SuperIO(SIO)和开源跨平台物联网框架ServerSuperIO(SSIO)构建系统的整体方案 3.C#工业物联网和集成系统解决方案的技术路线(数据源、数据采集、数据上传与接收、ActiveMQ、Mongodb、WebApi、手机App) 4.
662 0
Clion 跨平台的C++ IDE
CLion 是 JetBrains 推出的全新的 C/C++ 跨平台集成开发环境。 正式版本已经发出,目前是1.0.1  http://www.jetbrains.com/clion/ http://blog.
1274 0
【开源】C#跨平台物联网通讯框架ServerSuperIO(SSIO)
[连载]《C#通讯(串口和网络)框架的设计与实现》-1.通讯框架介绍 [连载]《C#通讯(串口和网络)框架的设计与实现》-2.框架的总体设计   目       录 C#跨平台物联网通讯框架ServerSuperIO(SSIO)正式开源... 1 1.      SSIO的特点 2.      SSIO概述 3.      SSIO与SIO的区别 4.      控制模式 5.      跨平台Windows和Linux   一.SSIO的特点 轻型高性能通信框架,适用于多种应用场,轮询模式、自控模式、并发模式和单例模式。
843 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
4479 0
《PhoneGap精粹:构建跨平台的移动App》——导读
本节书摘来自异步社区《PhoneGap精粹:构建跨平台的移动App》一书中的目录,作者 【美】John M. Wargo,更多章节内容可以访问云栖社区“异步社区”公众号查看
1073 0
datapump跨平台升级迁移的对比测试和优化
    目前计划对跨平台的数据库环境进行迁移,一来降低运维成本,二来更加可控。其实对于很多机器来说,如果机器跑了很多年,一直没有重启过,那么时间长了,一个直观的感受就是稳定,这也是小机口碑远远好于PC的一个重要原因吧,但是如果机器有一天出了问题,那么可能就会让大家坐立不安。
754 0
+关注
淘朗英
专注于高性能H5内核研发,用C/C++挖掘设备硬件的潜能,让JS提升开发效率,打造一种所见即所得的开发方式。
24
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载