zoukankan      html  css  js  c++  java
  • JavaScriptCore in swift

    JavaScriptCore是IOS7之后苹果悄悄推出的一个框架,用于Javascript与objective-c/swift互通。让Javascript开发者可以轻松愉快地用Javascript编写应用程序。

             根据我学习的原则,新东西学习,就一起学吧,所以边学swift边学Javascript,于是就用swift来折腾折腾JavaScriptCore。

    一、先看看JavaScriptCore框架的头文件:

    1 #import "JSContext.h"
    2 #import "JSValue.h"
    3 #import "JSManagedValue.h"
    4 #import "JSVirtualMachine.h"
    5 #import "JSExport.h"
    JSContext:Javascript的运行环境,一个JSContext就是一个Javascript的一个运行环境,也叫做作用域;个人理解,这个东西就是你有swift里的一个Javascript运行环境。
    JSValue:JSContext里的不同的Javascript值都可以封闭在JSVslue的对象里,包括字符串、数值、数组、函数等,甚至还有Error以及null和undefined;同时这个类型的对象可以方便快速地转化为swift里常用的数据类型,如toBool()、toUInt32()、toArray()、toDictionary()等;简单地说就是Javascript中的数据类型与swift中的数据类型相互转化的一个中间数据类型
    附:JavaScript与objective-c/swift中的数据类型对应表
     1    Objective-C type  |   JavaScript type
     2  --------------------+---------------------
     3          nil         |     undefined
     4         NSNull       |        null
     5        NSString      |       string
     6        NSNumber      |   number, boolean
     7      NSDictionary    |   Object object
     8        NSArray       |    Array object
     9         NSDate       |     Date object
    10        NSBlock (1)   |   Function object (1)
    11           id (2)     |   Wrapper object (2)
    12         Class (3)    | Constructor object (3)
    JSManagedValue:该类型主要是作为一个引用桥接,将JSValue转为JSManagedValue类型后,可以添加到JSVirtualMachine对象中,这样能够保证你在使用过程中JSValue对象不会被释放掉,当你不再需要该JSValue对象后,从JSVirtualMachine中移除该JSManagedValue对象,JSValue对象就会被释放并置空。
    JSVirtualMachine:JSVirtualMachine就是一个用于保存弱引用对象的数组,加入该数组的弱引用对象因为会被该数组retain,所以保证了使用时不会被释放,当数组里的对象不再需要时,就从数组中移除,没有了引用的对象就会被系统释放;

    JSExport:JSExport是一个协议,让JSContext运行环境中的JavaScript 可以识别该协议中定义的实例方法、类方法、属性等,让objective-c/swift与JavaScript能够自动交互;

    二、框架的整体结构大概了解一二了,接下来动手写写代码

     var context:JSContext = JSContext()//JSContext就是一个JS运行环境
     1 context.evaluateScript("var number = 5 + 5")//往运行环境里加整形变量
     2         context.evaluateScript("var names = ['Grace','Joe','Mike']")//数组
     3         context.evaluateScript("var triple = function(value){return value * 4}")//方法
     4         var tripleNumber:JSValue = context.evaluateScript("triple(number)")
     5         println("(tripleNumber)")//40
     6         let ocnames:JSValue = context.objectForKeyedSubscript("names")//取出JS运行环境里的数组(为JSValue类型)
     7         var len = ocnames.objectForKeyedSubscript("length")//取出来的JSValue遵循JavaScript中的数组属性,所以可以直接取到JavaScript数组中的“length”取得数组中的长度
     8         println("ocnames:(ocnames) count: (len.toInt32())")//ocnames:Grace,Joe,Mike count: 3 JSValue转化为swift中的Int:len.toInt32()
     9         ocnames.setObject("himily", atIndexedSubscript: 8)
    10         len = ocnames.objectForKeyedSubscript("length")
    11         println("after set 'himily' at indext 8 ocnames:(ocnames) count: (len.toInt32())")//after set 'himily' at indext 8 ocnames:Grace,Joe,Mike,,,,,,himily count: 9 取出来的JSValue遵循JavaScript中的数组属性,无下标越界,自动延展数组大小
    12         let firstName:JSValue = ocnames.objectAtIndexedSubscript(1)//取出JS运行环境里的数组中指定下标的元素
    13         println("first name = (firstName)")//first name = Joe
    14         let tripleFun:JSValue = context.objectForKeyedSubscript("triple")//取出JS运行环境中的方法
    15         let tripleResult = tripleFun.callWithArguments([9])//调用JS运行环境 中的方法

    代码注释已经很清楚,从上面代码我们总结几点:

    1、在OC/swift里,所有JavaScript代码都需要在JavaScript运行环境(JSContext)中通过evaluateScript运行;

    2、在OC/swift里,所有JavaScript中的方法、对象、属性都需要通过objectForKeyedSubscript来取得,取得所有对象均为JSValue类型

    3、通过objectForKeyedSubscript取得的JavaScript中的对象,都遵循该对象在JavaScript中有的所有特性,如上述代码中数组的长度,无数组越界,自动延展的特性

    4、通过objectForKeyedSubscript取得的JavaScript中的方法,均可以通过callWithArguments传入参数调用JavaScript中的方法并返回正确的结果(类型仍然为JSValue)


    三、JacaScript环境中异常检测
    1   //异常处理 用于检查和记录语法、类型和运行时的错误,该闭包可以检测当前context中所有JavaScript的出错
    2         context.exceptionHandler = {context,exception in
    3             println("JS Error:(exception)")
    4         }
    5         context.evaluateScript("function sqare(value1,value2){return value1 * value2")//JS Error:SyntaxError: Unexpected end of script 检测到语法出错  没有右边的“}”
    6          context.evaluateScript("function sqare(value1,value2){return value1 * value2}")

    在OC中exceptionHandler是block 在swift中exceptionHandler是闭包,用于检测当然JSContext运行环境中所有JavaScript代码的语法错误。如上述例子抛出了语句没结束的异常;

    四、JSExport

    创建一个继承JSExport协议的PersonJSExports

    1 @objc protocol PersonJSExports:JSExport{
    2     var firstName:String{get set}
    3     var lastName:String{get set}
    4     var birthYear:NSNumber?{get set}
    5 }

    创建一个Person,让它继承PersonJSExports协议

     1 @objc class Person:NSObject,PersonJSExports{
     2     dynamic var firstName:String
     3     dynamic var lastName:String
     4     dynamic var birthYear:NSNumber?
     5     init(firstName:String,lastName:String){
     6         self.firstName = firstName
     7         self.lastName = lastName
     8         self.birthYear = NSNumber(int: 1986)
     9     }
    10 }

    创建一个Person的对象实例,记其成为contex环境中的“Person”

     1     var personal:Person = Person(firstName: "lily", lastName: "king")
     2         context.setObject(personal, forKeyedSubscript:"Person")//把Person类导出到JS中去
     3         var p:JSValue = context.objectForKeyedSubscript("Person")
     4         var first:JSValue = p.objectForKeyedSubscript("firstName")
     5         var last:JSValue = p.objectForKeyedSubscript("lastName")
     6         var year:JSValue = p.objectForKeyedSubscript("birthYear")
     7 
     8         println("p.first:(first) p.last:(last) p.year:(year)")//这里面有个incorporate一词值得推敲,经过验证只有直接继承了JSExport的中自定义协议(@protocol)(定义的属性、方法)才能在JSContext中访问到 分两种情况 1.如果PersonJSExports(直接继承JSExport)里有定义firstName、lastName则打印出 p.first:lily p.last:king 2、如果PersonJSExports里没有定义firstName、lastName则打印出p.first:undefined p.last:undefined
     9         personal.firstName = "lucy"
    10         personal.lastName = "queen"
    11         personal.birthYear = NSNumber(int: 1999)
    12         var first1:JSValue = p.objectForKeyedSubscript("firstName")
    13         var last1:JSValue = p.objectForKeyedSubscript("lastName")
    14         var year1:JSValue = p.objectForKeyedSubscript("birthYear")
    15         println("aftter change p.first:(first1) p.last:(last1) p.year:(year1)")//经过验证只有直接继承了JSExport的中自定义协议(@protocol)(定义的属性、方法)才能在JSContext中访问到 分两种情况 1.如果PersonJSExports(直接继承JSExport)里有定义firstName、lastName则打印出 p.first:lucy p.last:queen 2、如果PersonJSExports里没有定义firstName、lastName则打印出aftter change p.first:undefined p.last:undefined full name undefined

    由上面代码的输出可以看到,只有直接继承了JSExport的中自定义协议(@protocol)(定义的属性、方法)才能在JSContext中访问到。

    关于JSExport中定义的方法,用OC来写一段

    定义一个UIButtonJsexport让其直接继承JSExport协议

    1 @protocol UIButtonJsexport <JSExport>
    2 
    3 - (void)setTitle:(NSString *)title forState:(UIControlState)state;
    4 
    5 @end

    创建 一个button,同时把UIButton添加一个UIButtonJsexport协议class_addProtocol([UIButton class], @protocol(UIButtonJsexport));

     1  class_addProtocol([UIButton class], @protocol(UIButtonJsexport));
     2     UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 100, 40)];
     3     [button setTitle:@"objective-c" forState:UIControlStateNormal];
     4     [button setBackgroundColor:[UIColor blueColor]];
     5     [self.view addSubview:button];
     6     context = [[JSContext alloc] init];
     7     [context setObject:button forKeyedSubscript:@"button"];
     8     context.exceptionHandler = ^(JSContext *con,JSValue *exception){
     9         con.exception = exception;
    10         NSLog(@"JS error:%@",exception);
    11     };
    12     [button addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside];

    点击的时候在context中改变button的title

    1 -(void)click
    2 {
    3     [context evaluateScript:@"button.setTitleForState('JavaScript',0)"];
    4 }

    当运行点击UIButton时就会看到button的title被改变了,也证明了对于已定义的类,也可以在运行时添加神奇的JSExport协议让它们可以在Objective-C和JavaScript直接实现友好互通。

  • 相关阅读:
    git命令
    WPF让绑定支持表达式
    WPF多语言动态切换的一种方法
    C#监测系统事件,睡眠状态
    记一次渗透测试
    Relay
    ECShop相关漏洞复现/分析
    人工智能学习路线图
    抽奖算法
    关于微信开发的 appid,openid,unionid
  • 原文地址:https://www.cnblogs.com/yanyan1119/p/4262962.html
Copyright © 2011-2022 走看看