JavaScriptCore in iOS
2017-03-23 16:24
585 查看
原文链接
可参考的文章:http://blog.csdn.net/longshihua/article/details/51645575
JSContext
/ JSValue
JSContext是运行 JavaScript 代码的环境。一个
JSContext是一个全局环境的实例,如果你写过一个在浏览器内运行的
JavaScript,
JSContext类似于
window。创建一个
JSContext后,可以很容易地运行
JavaScript 代码来创建变量,做计算,甚至定义方法:
SwiftObjective-C
JSContext *context = [[JSContext alloc] init]; [context evaluateScript:@"var num = 5 + 5"]; [context evaluateScript:@"var names = ['Grace', 'Ada', 'Margaret']"]; [context evaluateScript:@"var triple = function(value) { return value * 3 }"]; JSValue *tripleNum = [context evaluateScript:@"triple(num)"];
代码的最后一行,任何出自
JSContext的值都被包裹在一个
JSValue对象中。像
JavaScript 这样的动态语言需要一个动态类型,所以
JSValue包装了每一个可能的 JavaScript 值:字符串和数字;数组、对象和方法;甚至错误和特殊的 JavaScript 值诸如
null和
undefined。
JSValue包括一系列方法用于访问其可能的值以保证有正确的 Foundation 类型,包括:
JavaScript Type | JSValuemethod | Objective-C Type | Swift Type |
---|---|---|---|
string | toString | NSString | String! |
boolean | toBool | BOOL | Bool |
number | toNumber toDouble toInt32 toUInt32 | NSNumber double int32_t uint32_t | NSNumber! Double Int32 UInt32 |
Date | toDate | NSDate | NSDate! |
Array | toArray | NSArray | [AnyObject]! |
Object | toDictionary | NSDictionary | [NSObject : AnyObject]! |
Object | toObject toObjectOfClass: | custom type | custom type |
tripleNum的值,只需使用适当的方法:
SwiftObjective-C
NSLog(@"Tripled: %d", [tripleNum toInt32]); // Tripled: 30
下标值
对 JSContext和
JSValue实例使用下标的方式我们可以很容易地访问我们之前创建的
context的任何值。
JSContext需要一个字符串下标,而
JSValue允许使用字符串或整数标来得到里面的对象和数组:
SwiftObjective-C
JSValue *names = context[@"names"]; JSValue *initialName = names[0]; NSLog(@"The first name: %@", [initialName toString]); // The first name: Grace
Swift 展示了它的青涩,在这里,Objective-C 代码可以利用下标表示法,Swift 目前只公开原始方法来让下标成为可能:
objectAtKeyedSubscript()和
objectAtIndexedSubscript()。
调用方法
JSValue包装了一个 JavaScript 函数,我们可以从 Objective-C / Swift 代码中使用 Foundation 类型作为参数来直接调用该函数。再次,JavaScriptCore 很轻松的处理了这个桥接:
SwiftObjective-C
JSValue *tripleFunction = context[@"triple"]; JSValue *result = [tripleFunction callWithArguments:@[@5] ]; NSLog(@"Five tripled: %d", [result toInt32]);
错误处理
JSContext还有另外一个有用的招数:通过设置上下文的
exceptionHandler属性,你可以观察和记录语法,类型以及运行时错误。
exceptionHandler是一个接收一个
JSContext引用和异常本身的回调处理:
SwiftObjective-C
context.exceptionHandler = ^(JSContext *context, JSValue *exception) { NSLog(@"JS Error: %@", exception); }; [context evaluateScript:@"function multiply(value1, value2) { return value1 * value2 "]; // JS Error: SyntaxError: Unexpected end of script
JavaScript 调用
现在我们知道了如何从 JavaScript 环境中提取值以及如何调用其中定义的函数。那么反向呢?我们怎样才能从 JavaScript 访问我们在 Objective-C 或 Swift 定义的对象和方法?让
JSContext访问我们的本地客户端代码的方式主要有两种:block 和
JSExport协议。
Blocks
当一个 Objective-C block 被赋给 JSContext里的一个标识符,JavaScriptCore 会自动的把 block 封装在 JavaScript 函数里。这使得在 JavaScript 中可以简单的使用 Foundation 和 Cocoa 类,所有的桥接都为你做好了。见证了
CFStringTransform的强大威力,现在让我们来看看
JavaScript:
SwiftObjective-C
context[@"simplifyString"] = ^(NSString *input) { NSMutableString *mutableString = [input mutableCopy]; CFStringTransform((__bridge CFMutableStringRef)mutableString, NULL, kCFStringTransformToLatin, NO); CFStringTransform((__bridge CFMutableStringRef)mutableString, NULL, kCFStringTransformStripCombiningMarks, NO); return mutableString; }; NSLog(@"%@", [context evaluateScript:@"simplifyString('안녕하새요!')"]);
在这儿,Swfit 还有一个坑,请注意,这仅适用于 Objective-C 的 block,而不是 Swift 的闭包。要在
JSContext中使用 Swift 闭包,它需要(a)与
@ objc_block属性一起声明,以及(b)使用 Swift 那个令人恐惧的
unsafeBitCast()函数转换为
AnyObject。
内存管理
由于 block 可以保有变量引用,而且 JSContext也强引用它所有的变量,为了避免强引用循环需要特别小心。避免保有你的
JSContext或一个
block 里的任何
JSValue。相反,使用
[JSContext currentContext]得到当前上下文,并把你需要的任何值用参数传递。
JSExport
协议
另一种在 JavaScript 代码中使用我们的自定义对象的方法是添加 JSExport协议。无论我们在
JSExport里声明的属性,实例方法还是类方法,继承的协议都会自动的提供给任何
JavaScript 代码。我们将在下一节看到。
JavaScriptCore 实战
让我们做一个使用了所有这些不同的技术的示例 - 我们将定义一个 Person模型符合
JSExport子协议
PersonJSExports的例子,然后使用
JavaScript 从 JSON 文件中创建并填充实例。都有一个完整的 JVM 在那儿了,谁还需要
NSJSONSerialization?
1) PersonJSExports
和 Person
我们的 Person类实现了
PersonJSExports协议,该协议规定哪些属性在 JavaScript 中可用。
由于 JavaScriptCore 没有初始化,所以
create...类方法是必要的,我们不能像原生的 JavaScript 类型那样简单地用
var person = new Person()。
SwiftObjective-C
// in Person.h ----------------- @class Person; @protocol PersonJSExports <JSExport> @property (nonatomic, copy) NSString *firstName; @property (nonatomic, copy) NSString *lastName; @property NSInteger ageToday; - (NSString *)getFullName; // create and return a new Person instance with `firstName` and `lastName` + (instancetype)createWithFirstName:(NSString *)firstName lastName:(NSString *)lastName; @end @interface Person : NSObject <PersonJSExports> @property (nonatomic, copy) NSString *firstName; @property (nonatomic, copy) NSString *lastName; @property NSInteger ageToday; @end // in Person.m ----------------- @implementation Person - (NSString *)getFullName { return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName]; } + (instancetype) createWithFirstName:(NSString *)firstName lastName:(NSString *)lastName { Person *person = [[Person alloc] init]; person.firstName = firstName; person.lastName = lastName; return person; } @end
2) JSContext
配置
之前,我们可以用我们已经创建的 Person类,我们需要将其导出到 JavaScript 环境。我们也将借此导入 Mustache
JS library,我们将应用模板到我们的
Person对象。
// export Person class context[@"Person"] = [Person class]; // load Mustache.js NSString *mustacheJSString = [NSString stringWithContentsOfFile:... encoding:NSUTF8StringEncoding error:nil]; [context evaluateScript:mustacheJSString];
3) JavaScript 数据和进程
下面就来看看我们简单的 JSON 例子,这段代码将创建新的 Person实例。
注意:JavaScriptCore 转换的 Objective-C / Swift 方法名是 JavaScript 兼容的。由于 JavaScript 没有参数 名称,任何外部参数名称都会被转换为驼峰形式并且附加到函数名后。在这个例子中,Objective-C 的方法
createWithFirstName:lastName:变成了在JavaScript中的
createWithFirstNameLastName()。
JavaScriptJSON
var loadPeopleFromJSON = function(jsonString) { var data = JSON.parse(jsonString); var people = []; for (i = 0; i < data.length; i++) { var person = Person.createWithFirstNameLastName(data[i].first, data[i].last); person.birthYear = data[i].year; people.push(person); } return people; }
4) 加到一起
剩下的就是加载 JSON 数据,调用 JSContext将数据解析成
Person对象的数组,并用 Mustache 模板呈现每个
Person:
SwiftObjective-C
// get JSON string NSString *peopleJSON = [NSString stringWithContentsOfFile:... encoding:NSUTF8StringEncoding error:nil]; // get load function JSValue *load = context[@"loadPeopleFromJSON"]; // call with JSON and convert to an NSArray JSValue *loadResult = [load callWithArguments:@[peopleJSON]]; NSArray *people = [loadResult toArray]; // get rendering function and create template JSValue *mustacheRender = context[@"Mustache"][@"render"]; NSString *template = @"{{getFullName}}, born {{birthYear}}"; // loop through people and render Person object as string for (Person *person in people) { NSLog(@"%@", [mustacheRender callWithArguments:@[template, person]]); } // Output: // Grace Hopper, born 1906 // Ada Lovelace, born 1815 // Margaret Hamilton, born 1936
相关文章推荐
- [转]Running JavaScript in an iOS application with JavaScriptCore
- How to use insert or retrieve data by using Core Data in iOS
- Comparing JSValue representations in JavaScriptCore. A short overview
- Beginning Core Image in iOS 6
- iOS js oc相互调用(JavaScriptCore)(二)
- Sending iOS Push Notifications via APNS in JavaScript
- [iOS] 在桌面應用程式中使用 WebKit (2) - JavaScriptCore
- iOS开发日记28-详解JavaScriptCore
- iOS插件化研究之一——JavaScriptCore
- iOS插件化研究之一——JavaScriptCore
- iOS js oc相互调用(JavaScriptCore)(二)
- ios上js引擎的使用,javascriptcore和v8
- [转]编译 JavaScriptCore For iOS
- IOS webView 与JavaScriptCore 框架结合使用
- Core Location Manager Changes in iOS 8
- Core Data in IOS
- JavaScriptCore and iOS 7
- Core Location Manager Changes in iOS 8(iOS8获取不到地理位置)
- iOS js oc相互调用(JavaScriptCore)(二)
- iOS js oc相互调用(JavaScriptCore)(二)