打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
利用 JavaScriptCore 让 Web 与 Native 交互更便捷

还记得以前在网页上用 iframe 指定一个 URL 并通过 native 的 - webView:shouldStartLoadWithRequest:navigationType: 回调方法拦截请求,再蛋疼的各种截取和判断字符串,结合 json 结构的字符串来获取 web 端发来的数据。
通过 - stringByEvaluatingJavaScriptFromString: 来拼接老长的字符串来传递数据给 web 端吗。。。

现在我们可以对这种方式说 「爱过~」 了。

先看一下 Objective-C 与 JavaScript 之间类型的对应关系

Objective-C type JavaScript type
nil undefined
NSNull null
NSString string
NSNumber number, boolean
NSDictionary Object object
NSArray Array object
NSDate Date object
NSBlock Function object
id Wrapper object
Class Constructor object

返回的 JSValue 转换为 Objective-C 类型的方法

123456789101112131415161718192021
- (BOOL)toBool;- (double)toDouble;- (int32_t)toInt32;- (uint32_t)toUInt32;- (NSNumber *)toNumber;- (NSString *)toString;- (NSDate *)toDate;- (NSArray *)toArray;- (NSDictionary *)toDictionary;- (id)toObject;- (id)toObjectOfClass:(Class)expectedClass;

我们主要使用的是 JSContext 这个类,下面通过几个简单的例子来演示。

首先需要加入 JavaScriptCore.framework 并引入。

1
#import <JavaScriptCore/JavaScriptCore.h>

Objective-C 调用纯 JavaScript

例如有一段计算阶乘的脚本

test.js
123456789
function factorial(n) {  if (n < 0){      return;  }  if (n === 0){      return 1;  }  return n * factorial(n - 1)};

将脚本文件拖入项目。

注意在项目的 TARGETS > Build Phases 中,把 *.js 文件从 Compile Sources 都拖到 Copy Bundle Resources,否则会有编译警告。

通过 - evaluateScript: 方法把脚本引入 JSContext 对象

123456
NSString *path = [[[NSBundle mainBundle] bundlePath]  stringByAppendingPathComponent:@"test.js"];NSString *testScript = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];self.context = [[JSContext alloc] init];[self.context evaluateScript:testScript];

通过 - objectForKeyedSubscript: 传入 function 的名称以取到该方法, 再通过 - callWithArguments: 传入参数并得到返回值(如果有)。

123456
NSNumber *inputNumber = [NSNumber numberWithInteger:[self.inputNumberTextField.text integerValue]];JSValue *function = [self.context objectForKeyedSubscript:@"factorial"];JSValue *result = [function callWithArguments:@[inputNumber]];NSNumber *number = [result toNumber];

JavaScript 调用 native 代码

  • JSExport

首先通过一个协议来关联 JavaScript 的 function 和 native 方法,

还可通过 JSExportAs(functionName, Selector); 来指定 function 的别名。

123456789101112
@protocol TestJSExport <JSExport>JSExportAs(functionNameForJS /** JavaScript function 的别名 */,- (void)handleFactorialCalculateWithNumber:(NSNumber *)number);- (void)pushToNextViewControllerWithTitle:(NSString *)title;@end

然后在 UIWebView 回调方法 - webViewDidFinishLoad: 中把 web 的 javaScriptContext 与我们用来操作的 JSContext 对象关联,并把 self 设为 JavaScript 调用 function 的对象。

12345
self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];// 以 JSExport 协议关联 native 的方法self.context[@"native"] = self;//此处有误,关联到 self 会造成 self 这个对象无法释放的问题,建议使用后面的 block 形式,或者关联到非 self 本身的对象。(weakSelf 也不行)

这样在 web 中我们就可以通过所注册的 native 对象来调用到 native 方法。

1234567
<textarea  id="input" style="font-size:10pt;color:black;"></textarea><input type="button" value="计算阶乘" onclick="native.functionNameForJS(input.value);" /><a id="push" href="#" onclick="native.pushToNextViewControllerWithTitle('Next VC');">    push to next ViewController</a>

建议使用后面的 block 形式,或者关联到非 self 本身的对象。(weakSelf 也不行)

  • Block

同样的,先关联 javaScriptContext ,然后直接给 JSContext 对象 - setObject:forKeyedSubscript: 来设定相应 block。

12345
self.context[@"log"] =^(NSString *str){    NSLog(@"%@", str);};

这样在 web 中我们就可以直接调用上面注册的 function

1
<input type="button" value="测试log" onclick="log('测试');" />

使用 Block 要注意避免循环引用

  • 捕获异常

JSContextexceptionHandler 属性赋予相应 Block

123456
self.context.exceptionHandler =^(JSContext *context, JSValue *exceptionValue){    context.exception = exceptionValue;    NSLog(@"%@", exceptionValue);};

我们可以来利用一些开源的 JavaScript 库来做一些比 native 代码更方便和易于实现的东西

随便找了一个 Highcharts 图表库来做例子。

注意:Highcharts 开源但不完全免费。个人用户及非商业用途免费,商业用途需要购买许可。

大致的调用代码如下

123456789
NSArray *the1024Data = @[@33, @41, @32, @51, @42, @103, @136];NSDictionary *the1024Dict = @{@"name": @"1024", @"data": the1024Data};NSArray *theCCAVData = @[@8, @11, @21, @13, @20, @52, @43];NSDictionary *theCCAVDict = @{@"name": @"CCAV", @"data": theCCAVData};NSArray *seriesArray = @[the1024Dict, theCCAVDict];[self.context[@"drawChart"] callWithArguments:@[seriesArray]];
1234
function drawChart (seriesArray){  ...}

详细的代码就不贴了,见项目 JavaScriptCoreSample

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
有空看看 IOS7开发~JavaScriptCore
h5与App原生交互方案
论 iOS 开发中 JS 与 Native 的交互方式
Category: iOS插件化 | chentoo's blog
iOS js oc相互调用(JavaScriptCore)(二)
JSPatch技术文档
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服