iOS插件化研究之中的一个——JavaScriptCore
原文:p=191">http://chentoo.com/?p=191
一、前言
一样的开篇问题,为什么要研究这个?iOS为什么要插件化?为什么要借助其它语言比方html5 js甚至脚本lua等来实现原本OC/Swift应该实现的东西?
原因能够归结为两点:
1. iOS平台 appstore 审核速度不可控,而非常多活动页面须要频繁更新,假设每次更新都走appstore审核流程,那活动也就不要做了。
2. 可多平台复用代码,节省开发成本。
比方同一个活动的页面,用html5+js完毕,就能够通用的在iOS Android平台上。而仅仅须要维护一份html5+js代码。
现现在国内各大互联网公司的iOS端产品。绝大多是都有使用这样的技术。特别是html5+js。而使用脚本语言来做动态更新的app也不在少数。
本文先讨论使用html5 + js来插件化的技术。
请浏览一篇文章来脑补一下,我们要做啥 分析支付宝client的插件机制
当然,现在的支付宝版本号已经告别了这样的显性插件化的机制。后面会详细说。
另外还要补充一个前提。我们绝对不做纯html5+js的app,由于稍复杂的app,使用纯html5的方式,仅仅会给自己挖坑,现阶段,native+部分简单逻辑的html5才是真正切合实际的方案。这个问题不展开讨论了。
二、应该准备点什么?
首先我们得准备点东西。当然你要熟悉OC语言(swift亦可)。然后你要了解html语言,能写几句js。
然后我们绝不用历史上”著名”的PhoneGap来做。由于它真的非常弱。也不能简单的使用UIWebViewDelegate的一个方法来做简单的js 和 OC的通信,由于那是远远不够的。
我们要使用的是非常早就出现并广泛运用在mac平台。但直到iOS7才进入移动平台的JavaScriptCore。
这真的是iOS7開始原生提供的,真的不是私有的。真的你随便用。
三、JavaScriptCore基础知识
3.1 JavaScriptCore是什么?
JavaScriptCore框架是基于webkit中以C/C++实现的JavaScriptCore的一个包装。之前广泛应用于mac平台,从iOS7開始。apple主动将其添加到iOS SDK中。
JavaScriptCore让Objective-C和JavaScript代码的交互变得更加简单和直接。
JavaScriptCore中有几个重要的东西:
#import "JSContext.h"
#import "JSValue.h"
#import "JSManagedValue.h"
#import "JSVirtualMachine.h"
#import "JSExport.h"
他们都是日常使用中经经常使用到的东西。后面会结合实例,介绍他们都是干嘛的。
3.2 iOS怎样使用JavaScriptCore?
在须要的地方,引入:
#import JavaScriptCore/JavaScriptCore.h
3.3 JavaScriptCore能用来做什么?
3.3.1 通过OC运行js方法或调取js属性。
比方以下的一个样例:
JSContext *context = [[JSContext alloc] init];
[context evaluateScript:@"var arr = [1, 2, 'This is js string'];var sum = function(a, b) { return a+b;}"];
JSValue *jsArray = context[@"arr"];
JSValue *jsSum = context[@"sum"];
JSValue *jsSumResult = [jsSum callWithArguments:[NSArray arrayWithObjects:@12, @33, nil]];
JSContext对象是JS的运行环境。通过 -evaluateScript 方法能够运行一段javaScript。javaScript的全部变量方法都会在JSContext对象中妥善的保存。通过对JSContext对象的一些操作,能够调用javaScript的方法,或者存取javaScript的对象。
之前见到的JSVirtualMachine顾名思义,是javaScript的虚拟机,是为JSContext提供运行资源。
JSVirtualMachine的详细使用在后面也会讲到。
可是我们的JSContext *context 是通过 init方法生成的啊。看似并没有搀和到JSVirtualMachine啊?可是事实上,通过init方法生成的JSContext对象,在init方法内部,仍然会自己主动线生成一个JSVirtualMachine。然后调用JSContext 对象的 -initWithVirtualMachine 方法。故,一个JSContext对象,必然要相应着一个JSVirtualMachine对象。
同一个JSVirtualMachine中的若干个JSContext能够互相交换方法对象等等,可是不同的JSVirtualMachine不能互相交换不论什么JSContext的资源。
下图来自苹果官方,非常形象的描写叙述了这一点。
2015-03-11 :(不知为何,苹果删除了官方文档中关于JavaScriptCore的部分。。擦咧。
。。)
JSValue是JavaScriptCore中一个重要的类。前面说到,我们使用JSContext 和 JSVirtualMachine 开拓了一个运行和保留javaScript的空间,而JSContext中javaScript的各个方法和属性相应着JSValue。
JSValue也是OC 和 javaScript 互相訪问和改动的中间体。全部OC 和 javaScript的跨语言操作都要通过JSValue一些方法进行。
比方我们演示样例代码里的,JSValue *jsArray。jsArray相应着javaScript中的一个 array对象:arr。所以我们能够对jsArray进行一些操作,从而操作javaScript 中的 arr。
比如:
jsArray[0];//1
jsArray[2];//This is js string
jsArray[1] = 49;//改动arr 的第二个元素。
jsArray[@"length"];//结果是3。调用js arr对象的方法。
又比方我们演示样例代码里的,jsSum,相应着sum function,因此我们能够通过操作jsSum从而调取javaScript中的 sum function。
JSValue *jsSumResult = [jsSum callWithArguments:[NSArray arrayWithObjects:@12, @33, nil]];
//jsSumResult = 45;
可见。我们能够方便的通过JSValue对象的 callWithArguments:方法来直接调取 js 的 function。
js function的多參数,在OC中,由NSArray组装而成。
3.3.2 通过js运行OC方法或调取OC属性。
苹果介绍。有两种方式能够方便的通过js 调用 OC:
- Block 用来调用方法。
- JSExport protocol 用来调用对象。
演示样例:
//我们有一个OC方法。提供给js调用
- (NSInteger)sumWithA:(NSInteger)a B:(NSInteger)b C:(NSInteger)c
{
return a + b + c;
}
- (void)jsToOcFunction
{
JSContext *context = [[JSContext alloc] init];
context[@"sumNums"] = ^(NSInteger a, NSInteger b, NSInteger c) {
return [self sumWithA:a B:b C:c];
};
JSValue *sum = [context evaluateScript:@"sumNums(7, 56, 22)"];
NSLog(@"sum %@", sum);//sum 85
}
在样例中。我们定义了一个OC方法:sumWithA:(NSInteger)a B:(NSInteger)b C:(NSInteger)c。提供给js调取使用。
在调取时。我们首先声明一个JSContext。然后对context 的sumNums 赋予了一个OC的Block,Block内运行了我们之前提供的OC方法。
然后我们通过context的 -evaluateScript方法运行了一个js方法。尝试调用OC函数。终于看到了调取成功的结果。
我们再举一个样例,来说明JSContext 在js 调用 OC方法中的重要性。
//尝试用OC来实现一个JavaScriptCore所不具有的Log
JSContext *context = [[JSContext alloc] init];
context[@"log"] = ^() {
NSLog(@"+++++++Begin Log+++++++");
NSArray *args = [JSContext currentArguments];
for (JSValue *jsVal in args) {
NSLog(@"%@", jsVal);
}
JSValue *this = [JSContext currentThis];
NSLog(@"this: %@",this);
NSLog(@"-------End Log-------");
};
[context evaluateScript:@"log('ider', [7, 21], { hello:'world', js:100 });"];
//Output:
// +++++++Begin Log+++++++
// ider
// 7,21
// [object Object]
// this: [object GlobalObject]
// -------End Log-------
这个经典的样例来自于 Ider的blog
样例中有两个关键点:
- [JSContext currentArguments] 类方法能够拿到js函数中的全部參数列表。
每一个參数在OC中也都用JSValue 描写叙述。
- [JSContext currentThis] 类方法能够拿到调用该方法的对象。
须要特别注意的是:
1. 不论在不论什么情况下。不要在Block中直接使用外面的JSValue对象, 而应该把JSValue当做參数来传进Block中。
2. 不论在不论什么情况下。不要在Block中直接使用外面的JSContext对象, 而应该使用 [JSContext currentContext]获取。
上面我们看到了js调取OC方法的样例,以下我们看js通过JSExport protocol调取OC属性的样例:
//使用rumtime 为一个系统控件UIButton添加JSExport protocol
@protocol UIButtonExport <JSExport>
- (void)setTitle:(NSString *)title forState:(UIControlState)state;
@end
- (void)changeTitle
{
class_addProtocol([UIButton class], @protocol(UIButtonExport));
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
[button setTitle:@"你好 OC" forState:UIControlStateNormal];
button.frame = CGRectMake(100, 100, 100, 100);
[self.view addSubview:button];
JSContext *context = [[JSContext alloc] init];
context[@"button"] = button;
[context evaluateScript:@"button.setTitleForState('你好 js', 0)"];
}
而除了上述通过runtime的方式添加JSExport protocol之外,还能够通过category的方式,比方:
//UIButton+js.h
#import <UIKit/UIKit.h>
#import <JavaScriptCore/JavaScriptCore.h>
@protocol UIButtonExport <JSExport>
- (void)setTitle:(NSString *)title forState:(UIControlState)state;
@end
@interface UIButton (js) <UIButtonExport>
@end
能够看到。假设想要在js中调用OC 的类或者对象的方法。须要将方法在JSExport protocol中声明。
以下再举一个更复杂的样例。
//book.js
//列出书的title
var bookTitleList = function(book1, book2) {
var book1Title = book1.title;
var book2Title = book2.title;
return 'list:'+ book1Title + book2Title;
};
//创建两本书的合订本
var makeBookFromTwoBooks = function(book1, book2) {
var title = book1.title + book2.title;
var page = book1.page + book2.page;
return Book.makeBookWithTitlePage(title, page);
};
//
// Book.h
#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>
@class Book;
@protocol BookExports <JSExport>
@property NSString *title;
@property NSInteger page;
+ (Book *)makeBookWithTitle:(NSString *)title page:(NSInteger)page;
@end
@interface Book : NSObject <BookExports>
@property NSString *title;
@property NSInteger page;
+ (Book *)makeBookWithTitle:(NSString *)title page:(NSInteger)page;
@end
//
// Book.m
#import "Book.h"
@implementation Book
+ (Book *)makeBookWithTitle:(NSString *)title page:(NSInteger)page
{
Book *book = [[Book alloc] init];
book.title = title;
book.page = page;
return book;
}
@end
- (void)moreExportsTest
{
JSContext *context = [[JSContext alloc] init];
NSString* path = [[NSBundle mainBundle] pathForResource:@"book" ofType:@"js"];
NSString *jsString = [NSString stringWithContentsOfFile:path encoding:NSStringEncodingConversionAllowLossy error:nil];
[context evaluateScript:jsString];
Book *book1 = [Book makeBookWithTitle:@"资治通鉴第一部" page:330];
Book *book2 = [Book makeBookWithTitle:@"资治通鉴第二部" page:520];
JSValue *fucntion = context[@"bookTitleList"];
JSValue *result = [fucntion callWithArguments:@[book1, book2]];
NSLog(@"result %@",result.toString);
//result list:资治通鉴第一部资治通鉴第二部
context[@"Book"] = [Book class];
JSValue *function = context[@"makeBookFromTwoBooks"];
JSValue *jsResult = [function callWithArguments:@[book1, book2]];
Book *newBook = [jsResult toObject];
NSLog(@"newBook title %@ page: %ld",newBook.title, (long)newBook.page);
//newBook title 资治通鉴第一部资治通鉴第二部 page: 850
}
第一段js,是我们的javascript文件。我们在当中定义了两个方法。
第二段。是我们定义了一个Book类。能够看到Book对象有两个属性:title 和 page。有一个类方法:+ (Book )makeBookWithTitle:(NSString )title page:(NSInteger)page,用来生成新的Book。这种方法将在js中被调用。
然后我们声明了,@protocol BookExports 。将须要在js中直接訪问的属性和方法放置进去。
最后。让我们的Book类遵循BookExports protocol(@interface Book : NSObject )。
这样一个可在js中直接訪问的类就生成完毕了。
最后一段是我们的运行代码。
首先将一段js代码载入斤JSContext中并运行。之后声明两个Book对象,book1 book2。将其作为參数传入js函数bookTitleList中。能够看到运行结果与预期同样。在js中,我们直接book1.title取出了OC对象的属性。
紧接着。我们运行了第二个js函数,在当中,用js调用了OC的Book的类方法,创建出了一个新的OC 的Book对象并返回。
到这里。样例就讲完了。能够看到通过奇妙的JavaScriptCore。OC语言和javascript语言差点儿畅通无阻。
而这将是我们以后构建可插件化的iOS架构的基础。
iOS插件化研究之中的一个——JavaScriptCore的更多相关文章
- Live555研究之中的一个 源码编译
Live555研究之中的一个 源代码编译 Live555 是一个为流媒体提供解决方式的跨平台的C++开源项目,它 ...
- Android应用程序插件化研究之AssertManager
最近在研究Android应用的插件化开发,看了好几个相关的开源项目.插件化都是在解决以下几个问题: 如何把插件apk中的代码和资源加载到当前虚拟机. 如何把插件apk中的四大组件注册到进程中. 如何防 ...
- iOS 插件化开发汇总 Small框架
应用插件化背景 目前很多应用功能越来越多,软件显得越来越臃肿.因此插件化就成了很多软件发展的必经之路,比如支付宝这种平台级别的软件: 页上密密麻麻的功能,而且还在增多,照这个趋势发展下去,软件包的大小 ...
- 基于.NET MVC的高性能IOC插件化架构(一)
最近闲下来,整理了下最近写的代码,先写写架构,后面再分享几个我自己写的插件 最近经过反复对比,IOC框架选择了Autofac,原因很简单,性能出众,这篇博文是我的各大IOC框架的性能测试:http:/ ...
- 基于.NET MVC的高性能IOC插件化架构
基于.NET MVC的高性能IOC插件化架构 最近闲下来,整理了下最近写的代码,先写写架构,后面再分享几个我自己写的插件 最近经过反复对比,IOC框架选择了Autofac,原因很简单,性能出众,这篇博 ...
- Android 插件化开发(四):插件化实现方案
在经过上面铺垫后,我们可以尝试整体实现一下插件化了.这里我们先介绍一下最简单的实现插件化的方案. 一.最简单的插件化实现方案 最简单的插件化实现方案,对四大组件都是适用的,技术面涉及如下: 1). 合 ...
- 又一开源项目爆火于GitHub,Android高级插件化强化实战
一.插件化起源 插件化技术最初源于免安装运行 Apk的想法,这个免安装的 Apk 就可以理解为插件,而支持插件的 app 我们一般叫 宿主. 想必大家都知道,在 Android 系统中,应用是以 Ap ...
- 如何写一个c++插件化系统
1.为什么需要插件化系统 “编程就是构建一个一个自己的小积木, 然后用自己的小积木搭建大系统”. 但是程序还是会比积木要复杂, 我们的系统必须要保证小积木能搭建出大的系统(必须能被组合),有必须能使各 ...
- 【iOS与EV3混合机器人编程系列之四】iOS_WiFi_EV3_Library 剖析之中的一个:WiFi UDP和TCP
在上一篇文章中.我们通过编写EV3 Port Viewer项目实现了iOS监測EV3的实时端口数据. 程序最核心的部分就是我们的开源码库iOS_WiFi_EV3_Library. 那么,在本文中,我们 ...
随机推荐
- perl5 第五章 文件读写
第五章 文件读写 by flamephoenix 一.打开.关闭文件二.读文件三.写文件四.判断文件状态五.命令行参数六.打开管道 一.打开.关闭文件 语法为open (filevar, file ...
- mac 下 安装 mongodb 数据库
1.在网上下载mongodb 安装包,官方网站 mongodb.org/downloads 2.将下载的安装文件放在指定目录下,例 User/电脑名/文件夹名... 3.解压安装包,如需改名,新建文件 ...
- 【已解决】谁动了我的CurrentPrincipal?求助我在给Artech的wcf petshop增加授权机制的时候遇到的问题。
这个问题已解决,是绑定设置的问题,主要还是因为我自己没有深入理解WCF绑定的安全机制.在这篇博客里面我来说说怎么解决的. 下载了Artech的wcf petshop源码(博文链接)并调试运行成功后,打 ...
- 修改SQL Server登录密码(使用SQL Server身份登录)
修改登录密码: http://blog.sina.com.cn/s/blog_631611220100iqao.html
- SqlBulkCopy的使用
1.问题:导入大数据量到数据库,用我们普通的SqlHelper来做是每插入一条都是打开连接关闭连接,这样太慢,因此我们会想到让SqlConnection一直打开直到所有数据都插入完成再关闭连接.但是根 ...
- Informatica 9.5.1 安装配置
Informatica 结构 1个或多个资源库(Respository) PowerCenter数据整合引擎是基于元数据驱动的,提供了基于数据驱动的元数据知识库(Repository),该元数据知识 ...
- Oracle_sqlload导数案例
文件地址:http://115.com/lb/5lbbut5jc6op 案例中的sql_load导数公用到5个文件,分别是bat.ctl.txt.log.bad 5个文件 bat文件: --用户名/用 ...
- iOS开发中遇到的bug
报错:The operation couldn’t be completed. (LaunchServicesError error 0.) 解决办法:重置模拟器
- JBPM6教程
JBPM6教程-手把手教你安装JBPM 1. 安装JBPM的先决条件: (1)JDK 1.6+以上,没有安装的话,猛击这里. (2)Ant 1.7+以上,没有安装的话,看看这里. 2. 下载JBPM安 ...
- c++设计模式总结 好久没写博客了 实在是忙
具体代码就不贴出来了 通俗易懂的理解方式 原创 c++设计模式: 简单工厂模式 工厂模式有一种非常形象的描述,建立对象的类就如一个工厂,而需要被建立的对象就是一个个产品:在工厂中加工产品 ...