在移动的混合开发中经常用到OC与JS的交互,就涉及iOS中的JavaScriptCore类,下面终结如下

JavaScriptCore中的类

在项目中引入JavaScriptCore后,链到头文件中,除了大段的Copyright注释可以看到里面只要引入了5个文件,每个文件里都定义跟文件名对应的类:

  • JSContext
  • JSValue
  • JSManagedValue
  • JSVirtualMachine
  • JSExport

虽说代码中的注释介绍的也比较详细了,但是如同一图顶万言,对程序员来说代码更有说服力。本文就先来说说这些类相对比较好理解但又很常用的JSContext和JSValue以及它们方法的使用方式和效果。

JSContext和JSValue

JSVirtualMachine为JavaScript的运行提供了底层资源,JSContext就为其提供着运行环境,通过- (JSValue *)evaluateScript:(NSString *)script;方法就可以执行一段JavaScript脚本,并且如果其中有方法、变量等信息都会被存储在其中以便在需要的时候使用。而JSContext的创建都是基于JSVirtualMachine- (id)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine;,如果是使用- (id)init;进行初始化,那么在其内部会自动创建一个新的JSVirtualMachine对象然后调用前边的初始化方法。

JSValue则可以说是JavaScript和Object-C之间互换的桥梁,它提供了多种方法可以方便地把JavaScript数据类型转换成Objective-C,或者是转换过去。其一一对应方式可见下表:

 
 
Objective-C JavaScript JSValue Convert JSValue Constructor
nil undefined   valueWithUndefinedInContext
NSNull null   valueWithNullInContext:
NSString string toString  
NSNumber number, boolean toNumber
toBool
toDouble
toInt32
toUInt32
valueWithBool:inContext:
valueWithDouble:inContext:
valueWithInt32:inContext:
valueWithUInt32:inContext:
NSDictionary Object object toDictionary valueWithNewObjectInContext:
NSArray Array object toArray valueWithNewArrayInContext:
NSDate Date object toDate  
NSBlock Function object    
id Wrapper object toObject
toObjectOfClass:
valueWithObject:inContext:
Class Constructor object    

基本类型转换

先看个简单的例子:

1     JSContext *context = [[JSContext alloc] init];
//iOS调用JS
2 JSValue *jsVal = [context evaluateScript:@"21+7"];
3 int iVal = [jsVal toInt32];
4 NSLog(@"JSValue: %@, int: %d", jsVal, iVal);
5
6 //Output:
7 // JSValue: 28, int: 28

很简单吧,还可以存一个JavaScript变量在JSContext中,然后通过下标来获取出来。而对于Array或者Object类型,JSValue也可以通过下标直接取值和赋值。

 1     JSContext *context = [[JSContext alloc] init];
2 [context evaluateScript:@"var arr = [21, 7 , 'iderzheng.com'];"];//让JS创建一个数组对象
3 JSValue *jsArr = context[@"arr"]; // Get array from JSContext
4
5 NSLog(@"JS Array: %@; Length: %@", jsArr, jsArr[@"length"]);
6 jsArr[1] = @"blog"; // Use JSValue as array
7 jsArr[7] = @7;
8
9 NSLog(@"JS Array: %@; Length: %d", jsArr, [jsArr[@"length"] toInt32]);
10
11 NSArray *nsArr = [jsArr toArray];
12 NSLog(@"NSArray: %@", nsArr);
13
14 //Output:
15 // JS Array: 21,7,iderzheng.com Length: 3
16 // JS Array: 21,blog,iderzheng.com,,,,,7 Length: 8
17 // NSArray: (
18 // 21,
19 // blog,
20 // "iderzheng.com",
21 // "<null>",
22 // "<null>",
23 // "<null>",
24 // "<null>",
25 // 7
26 // )

通过输出结果很容易看出代码成功把数据从Objective-C赋到了JavaScript数组上,而且JSValue是遵循JavaScript的数组特性:无下标越位,自动延展数组大小。并且通过JSValue还可以获取JavaScript对象上的属性,比如例子中通过"length"就获取到了JavaScript数组的长度。在转成NSArray的时候,所有的信息也都正确转换了过去。

方法的转换

各种数据类型可以转换,Objective-C的Block也可以传入JSContext中当做JavaScript的方法使用。比如在前端开发中常用的log方法,虽然JavaScritpCore没有自带(毕竟不是在网页上运行的,自然不会有window、document、console这些类了),仍然可以定义一个Block方法来调用NSLog来模拟:

 1     JSContext *context = [[JSContext alloc] init];
2 context[@"log"] = ^() {
3 NSLog(@"+++++++Begin Log+++++++");
4
5 NSArray *args = [JSContext currentArguments];
6 for (JSValue *jsVal in args) {
7 NSLog(@"%@", jsVal);
8 }
9
10 JSValue *this = [JSContext currentThis];
11 NSLog(@"this: %@",this);
12 NSLog(@"-------End Log-------");
13 };
14
15 [context evaluateScript:@"log('ider', [7, 21], { hello:'world', js:100 });"];
16
17 //Output:
18 // +++++++Begin Log+++++++
19 // ider
20 // 7,21
21 // [object Object]
22 // this: [object GlobalObject]
23 // -------End Log-------

通过Block成功的在JavaScript调用方法回到了Objective-C,而且依然遵循JavaScript方法的各种特点,比如方法参数不固定。也因为这样,JSContext提供了类方法来获取参数列表(+ (JSContext *)currentContext;)和当前调用该方法的对象(+ (JSValue *)currentThis)。对于"this",输出的内容是GlobalObject,这也是JSContext对象方法- (JSValue *)globalObject;所返回的内容。因为我们知道在JavaScript里,所有全局变量和方法其实都是一个全局变量的属性,在浏览器中是window,在JavaScriptCore是什么就不得而知了。

Block可以传入JSContext作方法,但是JSValue没有toBlock方法来把JavaScript方法变成Block在Objetive-C中使用。毕竟Block的参数个数和类型已经返回类型都是固定的。虽然不能把方法提取出来,但是JSValue提供了- (JSValue *)callWithArguments:(NSArray *)arguments;方法可以反过来将参数传进去来调用方法。

 1     JSContext *context = [[JSContext alloc] init];
2 [context evaluateScript:@"function add(a, b) { return a + b; }"];
//获取JS中的方法
3 JSValue *add = context[@"add"];
4 NSLog(@"Func: %@", add);
5
//将参数传给JS的方法并执行该方法
6 JSValue *sum = [add callWithArguments:@[@(7), @(21)]];
7 NSLog(@"Sum: %d",[sum toInt32]);
8 //OutPut:
9 // Func: function add(a, b) { return a + b; }
10 // Sum: 28

JSValue还提供- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments;让我们可以直接简单地调用对象上的方法。只是如果定义的方法是全局函数,那么很显然应该在JSContextglobalObject对象上调用该方法;如果是某JavaScript对象上的方法,就应该用相应的JSValue对象调用。

异常处理

Objective-C的异常会在运行时被Xcode捕获,而在JSContext中执行的JavaScript如果出现异常,只会被JSContext捕获并存储在exception属性上,而不会向外抛出。时时刻刻检查JSContext对象的exception是否不为nil显然是不合适,更合理的方式是给JSContext对象设置exceptionHandler,它接受的是^(JSContext *context, JSValue *exceptionValue)形式的Block。其默认值就是将传入的exceptionValue赋给传入的contextexception属性:

1     ^(JSContext *context, JSValue *exceptionValue) {
2 context.exception = exceptionValue;
3 };

我们也可以给exceptionHandler赋予新的Block以便在JavaScript运行发生异常的时候我们可以立即知道:

 1     JSContext *context = [[JSContext alloc] init];
2 context.exceptionHandler = ^(JSContext *con, JSValue *exception) {
3 NSLog(@"%@", exception);
4 con.exception = exception;
5 };
6
7 [context evaluateScript:@"ider.zheng = 21"];
8
9 //Output:
10 // ReferenceError: Can't find variable: ider

使用Block的注意事项

从之前的例子和介绍应该有体会到Block在JavaScriptCore中起到的强大作用,它在JavaScript和Objective-C之间的转换 建立起更多的桥梁,让互通更方便。但是要注意的是无论是把Block传给JSContext对象让其变成JavaScript方法,还是把它赋给exceptionHandler属性,在Block内都不要直接使用其外部定义的JSContext对象或者JSValue,应该将其当做参数传入到Block中,或者通过JSContext的类方法+ (JSContext *)currentContext;来获得。否则会造成循环引用使得内存无法被正确释放。

比如上边自定义异常处理方法,就是赋给传入JSContext对象con,而不是其外创建的context对象,虽然它们其实是同一个对象。这是因为Block会对内部使用的在外部定义创建的对象做强引用,而JSContext也会对被赋予的Block做强引用,这样它们之间就形成了循环引用(Circular Reference)使得内存无法正常释放。
对于JSValue也不能直接从外部引用到Block中,因为每个JSValue上都有JSContext的引用 (@property(readonly, retain) JSContext *context;),JSContext再引用Block同样也会形成引用循环。

JavaScriptCore的更多相关文章

  1. iOS引入JavaScriptCore引擎框架(一)

    JavaScriptCore引擎     我们都知道WebKit是个渲染引擎,简单来说负责页面的布局,绘制以及层的合成,但是WebKit工程中不仅仅有关于渲染相关的逻辑,也集成了默认的javascri ...

  2. 判断js引擎是javascriptCore或者v8

    来由   纯粹的无聊,一直在搜索JavaScriptCore和SpiderMonkey的一些信息,却无意中学习了如何在ios的UIWebView中判断其js解析引擎的方法: if (window.de ...

  3. WKWebView与JS交互,UIWebView+JavascriptCore和JS交互

    最近一直在做有关Swift和JavaScript交互的程序,所以有关UIWebView和WKWebView在使用上的差别在此总结下: UIWebView: (1)创建 var webView: UIW ...

  4. 说说JavaScriptCore

    http://www.jianshu.com/p/1328e15416f3/comments/1724404 javascript目前看来仍是世界上最流行的语言,不管在web.服务端还是客户端都有广泛 ...

  5. iOS JavaScriptCore与H5交互时出现异常提示

    在利用JavaScriptCore与H5交互时出现异常提示: This application is modifying the autolayout engine from a background ...

  6. JavaScriptCore 使用

    JavaScriptCore JavaScriptCore是webkit的一个重要组成部分,主要是对JS进行解析和提供执行环境.代码是开源的,可以下下来看看(源码).iOS7后苹果在iPhone平台推 ...

  7. JavaScriptCore框架介绍

    http://www.cocoachina.com/ios/20140409/8127.html 这个框架其实只是基于webkit中以C/C++实现的JavaScriptCore的一个包装,在旧版本i ...

  8. iOS中JS 与OC的交互(JavaScriptCore.framework)

    iOS中实现js与oc的交互,目前网上也有不少流行的开源解决方案: 如:react native 当然一些轻量级的任务使用系统提供的UIWebView 以及JavaScriptCore.framewo ...

  9. iOS7新JavaScriptCore框架入门介绍

    前阵子,Apple正式发布了新的iOS 7系统,最大最直观的改变在于界面变得小清新范了,我也提到<iOS,你真的越来越像Android了>.不过对于移动开发者来说,除了要适应Xcode 5 ...

  10. iOS7 中的JavaScriptCore简单介绍

    以前写过一篇介绍如何使用第三方库在ios上进行js和oc交互调用的文章,链接如下 iOS 使用UIWebView把oc代码和javascript相关联.当时建立项目时,仍然是ios6时代,所以没有原生 ...

随机推荐

  1. PlayJava Day017

    今日所学: /* 2019.08.19开始学习,此为补档. */ 1.数组变量 a.数组变量是数组的管理者而非数组本身 b.数组必须创建出来然后交给数组变量来管理 c.数组变量之间的赋值是管理权限的赋 ...

  2. C#实现Java的DigestUtils.sha256Hex

    代码地址:DotNetCore_Practice/JavaToC#/DigestUtils at master · PuzzledAlien/DotNetCore_Practice public cl ...

  3. SSM框架之Mybatis(7)延迟加载、缓存及注解

    Mybatis(7)延迟加载.缓存及注解 1.延迟加载 延迟加载: 就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据.延迟加载也称懒加载. **好处:**先从单表查询,需要时再从关联表去关 ...

  4. .netcore 中使用开源的AOP框架 AspectCore

    AspectCore Project 介绍 什么是AspectCore Project ? AspectCore Project 是适用于Asp.Net Core 平台的轻量级 Aop(Aspect- ...

  5. HttpWebRquest.GetRequestStreamAsync()发生延迟

    前景提要:此问题不定适用Browser通信方式,主要提供给通信发生异常时解决思路. Q: request请求创建过程发生延迟 [Scene]: ​ .Net的HttpWebRquest.GetRequ ...

  6. sqlserver实现分隔字符串

    sqlserver 使用函数实现分隔字符串 create function dbo.fn_split ( @str_source nvarchar(max), ) ) returns @temp ta ...

  7. 好用的性能检测工具-性能监控工具- Glances

    平常我经常使用 htop 工具来进行对主机进行性能检测.但是它只能对 进行进行管理.并简要显示 进程和cpu和内存使用信息:性能监控工具: glances 是比较好的性能检测工具.相比较htop还能显 ...

  8. android tween animation合集

    自己写的一些tween animation动画xml文件,可用于activity切换,图片切换动画等 http://files.cnblogs.com/zj2012zy/anim.rar

  9. 踩坑---vue-cli搭建的项目中localhost不能访问

    只需要在config文件夹里面的index.js文件里面的module.exports下面的dev中的 host:'localhost' 改为 host:'0.0.0.0' ,然后重启服务器

  10. (转)玩转Koa -- koa-bodyparser原理解析

    地址:http://www.imooc.com/article/274059 一.前置知识   在理解koa-bodyparser原理之前,首先需要了解部分HTTP相关的知识. 1.报文主体   HT ...