iOS插件化研究之一——JavaScriptCore
2015-03-30 09:29
211 查看
iOS插件化研究之一——JavaScriptCore
一、前言
一样的开篇问题,为什么要研究这个?iOS为什么要插件化?为什么要借助其他语言比如html5 js甚至脚本lua等来实现原本OC/Swift应该实现的东西?原因可以归结为两点:
1. iOS平台 appstore 审核速度不可控,而很多活动页面需要频繁更新,如果每次更新都走appstore审核流程,那活动也就不要做了。
2. 可多平台复用代码,节省开发成本。比如同一个活动的页面,用html5+js完成,就可以通用的在iOS Android平台上,而只需要维护一份html5+js代码。
现如今国内各大互联网公司的iOS端产品,绝大多是都有使用这种技术,特别是html5+js。而使用脚本语言来做动态更新的app也不在少数。
本文先讨论使用html5 + js来插件化的技术。
请浏览一篇文章来脑补一下,我们要做啥 分析支付宝客户端的插件机制
当然,现在的支付宝版本已经告别了这种显性插件化的机制。后面会具体说。
另外还要补充一个前提,我们绝对不做纯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中有几个重要的东西:
123456 | #import "JSContext.h"#import "JSValue.h"#import "JSManagedValue.h"#import "JSVirtualMachine.h"#import "JSExport.h" |
3.2 iOS如何使用JavaScriptCore?
在需要的地方,引入:1 2 | #import JavaScriptCore/JavaScriptCore.h |
3.3 JavaScriptCore能用来做什么?
3.3.1 通过OC执行JS方法或调取JS属性。
比如下面的一个例子:
1234567 | 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 *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。
例如:
1 2 3 4 5 | jsArray[0];//1 jsArray[2];//This is js string jsArray[1] = 49;//修改arr 的第二个元素。 jsArray[@"length"];//结果是3,调用js arr对象的方法。 |
123 | JSValue *jsSumResult = [jsSum callWithArguments:[NSArray arrayWithObjects:@12, @33, nil]];//jsSumResult = 45; |
JSExport protocol 用来调用对象。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //我们有一个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 } |
在调取时,我们首先声明一个JSContext,然后对context 的sumNums 赋予了一个OC的Block,Block内执行了我们之前提供的OC方法。
然后我们通过context的 -evaluateScript方法执行了一个js方法,尝试调用OC函数。最终看到了调取成功的结果。
我们再举一个例子,来说明JSContext 在js 调用 OC方法中的重要性。
12345678910111213141516171819202122232425 | //尝试用OC来实现一个JavaScriptCore所不具有的LogJSContext *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------- |
[JSContext currentThis] 类方法可以拿到调用该方法的对象。
需要特别注意的是:1. 不论在任何情况下,不要在Block中直接使用外面的JSValue对象, 而应该把JSValue当做参数来传进Block中。
2. 不论在任何情况下,不要在Block中直接使用外面的JSContext对象, 而应该使用 [JSContext currentContext]获取。上面我们看到了js调取OC方法的例子,下面我们看js通过JSExport protocol调取OC属性的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | //使用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)"]; } |
1234567891011 | //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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //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); }; |
12345678910111213141516171819202122232425262728293031 | //// 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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | - (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 } |
第二段,是我们定义了一个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架构的基础。
转载自:http://www.chentoo.com/?p=191
相关文章推荐
- iOS插件化研究之中的一个——JavaScriptCore
- iOS插件化研究之一——JavaScriptCore
- iOS js oc相互调用(JavaScriptCore)
- iOS js oc相互调用(JavaScriptCore)(二)
- iOS js oc相互调用(JavaScriptCore)(二)
- IOS开发笔记之JavaScriptCore.framework
- iOS js oc相互调用(JavaScriptCore)(二)
- [转]Running JavaScript in an iOS application with JavaScriptCore
- iOS中使用JavaScriptCore实现Objective-C和JavaScript的相互调用
- iOS js oc相互调用(JavaScriptCore)
- ios上js引擎的使用,javascriptcore和v8
- iOS js oc相互调用(JavaScriptCore)
- [iOS] 在桌面應用程式中使用 WebKit (2) - JavaScriptCore
- 用JavaScriptCore做android和iOS都兼容的JS-NativeSDK(****)
- 在iOS6机子上运行 React-Native Demo, 自己编译 JavaScriptCore 源码,支持 iOS5.1 ,iOS 6
- iOS引入JavaScriptCore引擎框架(一)
- [转]编译 JavaScriptCore For iOS
- JavaScriptCore and iOS 7
- IOS webView 与JavaScriptCore 框架结合使用
- iOS js oc相互调用(JavaScriptCore)(二)