浅谈JavaScriptCore

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
查看查看174 回复回复1 收藏收藏 分享淘帖 转播转播 分享分享 微信
查看: 174|回复: 1
收起左侧

浅谈JavaScriptCore

[复制链接]
jswift 发表于 2016-10-24 10:33:22 | 显示全部楼层 |阅读模式
快来登录
获取优质的苹果资讯内容
收藏热门的iOS等技术干货
拷贝下载Swift Demo源代码
订阅梳理好了的知识点专辑

敏捷大拇指上看到《Swift使用JavaScriptCore与JS交互,让Native和HTML5共舞》,我也发一篇JavaScriptCore的资料。

OS X Mavericks 和 iOS 7 引入了 JavaScriptCore 库,它把 WebKit 的 JavaScript 引擎用 Objective-C 封装,提供了简单,快速以及安全的方式接入世界上最流行的语言。在项目的实际开发中,由于需要与 H5 端有交互,所以采用了JavaScriptCore的交互方式,借此参与该项目的机会,对JavaScriptCore也有了一些简单的了解。

浅谈JavaScriptCore

浅谈JavaScriptCore - 敏捷大拇指 - 浅谈JavaScriptCore





1、JSContext/JSValue

JSContext 是 JavaScript 的执行环境。所有 JavaScript 执行发生的背景,以及所有 JavaScript 值被绑在这一个上下文中。JSContext 类似于 UIWindow 的概念,所有的执行都在改环境中发生:

[Objective-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)"];
NSLog(@"Tripled: %d", [tripleNum toInt32]);
// Tripled: 30


代码最后一行,每个 JSValue 起源于 JSContext 和 持有它的强引用。当一个 JSValue 实例方法创造一个新的 JSValue时,该新 JSValue 起源于同一个 JSContext。 JSValue 包装了每一个可能的 JavaScript 值:字符串和数字;数组、对象和方法;甚至错误和特殊的 JavaScript 值诸如 null 和 undefined。

Objective-C type
JavaScript type
nilundefined
NSNullnull
NSStringstring
NSNumbernumber, boolean
NSDictionaryObject object
NSArrayArray object
NSDateDate object
NSBlock (1)Function object (1)
id (2)Wrapper object (2)
Class (3)Constructor object (3)





2、下标支持

作为下标传递的对象键将被转换为一个 JavaScript 值, 然后值转换为一个用作获取属性名称的字符串。

[Swift] 纯文本查看 复制代码
JSValue *names = context[@"names"];
JSValue *initialName = names[0];
NSLog(@"The first name: %@", [initialName toString]);
// The first name: Grace


该简易方法对应于以下两个方法:

[Objective-C] 纯文本查看 复制代码
- (JSValue *)objectForKeyedSubscript:(id)key;
- (JSValue *)objectAtIndexedSubscript:(NSUInteger)index;





3、调用方法

JSValue 包装了一个 JavaScript 函数,我们可以从 Objective-C 代码中使用 Foundation 类型作为参数来直接调用该函数。

[Objective-C] 纯文本查看 复制代码
JSValue *tripleFunction = context[@"triple"];
JSValue *result = [tripleFunction callWithArguments:@[@5] ];
NSLog(@"Five tripled: %d", [result toInt32]);





4、JavaScript 调用

上面说明的 OC 调用 JavaScript 的方法。那么接下来,说明 JavaScript 访问 OC 定义的对象和方法。我粗浅的认为就是在遵守该协议后,JavaScript 就可以调用 OC 实现的方法和属性等。

让 JSContext 访问我们的本地客户端代码的方式主要有两种:JSExport 协议和block。



4.1、JSExport 协议

JSExport 提供一个将 OC 中的类、实例方法和属性等导出为 JavaScript 函数的方法。

该协议只包含了一个方法:

[Objective-C] 纯文本查看 复制代码
JSExportAs(PropertyName, Selector)


即当导出到 JavaScript 时,重命名一个 selector。

这就需要熟悉它的做法,当带有一个或多个参数的 seletor 转变为 JavaScript 的属性名字时,在默认情况下一个属性名字将采用以下方式生成:

  • 所有的冒号将从 Selector 当中移除掉;
  • 任何一个后边跟着冒号的小写字母开头的单词将被改换为大写。


在这种默认的转换方式下,一个 selector 如 doFoo: withBar: 将被导为doFooWithBar 。这种默认的改变有可能被 JSExportAs 宏 所改写,例如将 doFoo: withBar: 导出为 doFoo 。实际写法如下:

[Objective-C] 纯文本查看 复制代码
@protocol MyClassJavaScriptMethods <JSExport>
    JSExportAs(doFoo,
    - (void)doFoo:(id)foo withBar:(id)bar
    );
@end


请注意 JSExport 宏只可能适用于带有一个或更多的参数的selector。



4.2、Blocks

当一个 Objective-C block 被赋给 JSContext 里的一个标识符,JavaScriptCore 会自动的把 block 封装在 JavaScript 函数里。如下:

[Objective-C] 纯文本查看 复制代码
JSContext *context = [[JSContext alloc] init];
    context[@"simplifyString"] = ^(NSString *input) {
        NSMutableString *mutableString = [input mutableCopy];
        CFStringTransform((__bridge CFMutableStringRef)mutableString, NULL, kCFStringTransformToLatin, NO);
        return mutableString;
    };
NSLog(@"%@", [context evaluateScript:@"simplifyString('什么!')"]);
// shén me!





5、JSManagedValue 与内存管理

由于 block 可以保有变量引用,而且 JSContext 也强引用它所有的变量,为了避免强引用循环需要特别小心。OC 采用的是引用计数机制,而 JavaScript采用的垃圾回收机制。基于此项机制,便出现了 JSManagedValue ,它的主要用途是存储一个 JSValue,这样可以避免一个保留环的产生。




6、JavaScript 与 OC 交互的实际实例

在开发项目中,我们采用了 JavaScript 的方式完成了与 Native 的交互,使得比单纯的运用 UIWebview 的 stringByEvaluatingJavaScriptFromString: 更加高效。

如上所述,让 JSContext 访问我们的本地客户端代码的方式主要有两种:JSExport 协议和block。在我们项目中主要采用了 JSExport 的方式去实现。



6.1、JDRWebViewJSExportProtocol 和 JDRWebViewBaseHandler

我们的 JDRWebViewBaseHandler 类实现了 JDRWebViewJSExportProtocol 协议,该协议规定了那些方法或者属性可在 JavaScript 中使用。

[Objective-C] 纯文本查看 复制代码
@protocol JDRWebViewJSExportProtocol <JSExport , NSObject>

JSExportAs
(closeForJS /**H5调用的 Webview 关闭方法的别名**/,
 @optional
 - (void)close:(id)JSON callback:(JSValue*) callback
 );

JSExportAs
(goBackForJS /**H5调用的 Webview 关闭方法的别名**/,
 @optional
 - (void)goBack:(id)JSON callback:(JSValue*) callback
 );
@end

@interface JDRWebViewBaseHandler : NSObject <JDRWebViewJSExportProtocol>
@property (nonatomic , weak) JDRWebViewController *webViewController;

-(instancetype)initWithWebViewController:(JDRWebViewController *) webViewController NS_DESIGNATED_INITIALIZER;
@end

@implementation JDRWebViewBaseHandler
-(instancetype)initWithWebViewController:(JDRWebViewController *) webViewController{
    if (self = [super init]) {
        _webViewController = webViewController;
    }
    return self;
}

-(void)goBack:(id)JSON callback:(JSValue *)callback {
    WEAK_SELF(weakSelf);
    dispatch_async(dispatch_get_main_queue(), ^{
        STRONG_SELF(strongSelf, weakSelf);
        //判断 backPressCallBack 是否已经设置 ,如果已经设置监听方法 , 则执行 监听回退的 JS 方法
        if ([strongSelf.webViewController.webView canGoBack]) {
            [(UIWebView*)strongSelf.webViewController.webView stopLoading];
            [(UIWebView*)strongSelf.webViewController.webView goBack];
        }else {
            [strongSelf close:nil callback:nil];
        }
    });
}
-(void)close:(id)JSON callback:(JSValue *)callback {
    //目前关闭 WebView 方法不关注 callback 参数
    WEAK_SELF(weakSelf);
    dispatch_async(dispatch_get_main_queue(), ^{
        STRONG_SELF(strongSelf, weakSelf);
        if ([strongSelf.webViewController.navigationController             popViewControllerAnimated:YES] == nil) {
                [strongSelf.webViewController                             dismissViewControllerAnimated:YES completion:nil];
            }
    });
}
@end




6.2、JSContext 配置

然后,我们可以用我们已经创建的 JDRWebViewBaseHandler 类,我们需要将其导出到 JavaScript 环境中。

[Objective-C] 纯文本查看 复制代码
self.JSHandler = [[JDRWebViewBaseHandler alloc] initWithWebViewController:self];    
// 以 JSExport 协议关联 native 的方法 *platform* 是暂时定义的关键字
self.context[@"platform"] = self.JSHandler;




6.3、在 H5 页面中书写 JavaScript 函数调用 OC

[JavaScript] 纯文本查看 复制代码
close: function() {
  platform.coselForJS();
}





7、结语

通常对于 JavaScript 与 OC 的交互,会采用 JavaScriptCore 包装一层协议方法,原始一点的方法则是通过截取 UIWebView 的协议实现。随着 iOS 8 之后 WKWebview的出现,又出现了一个新的方式,而对于 WKWebView 来说是不支持直接与 JavaScriptCore 交互的。通常采取 WKUserContentController 采用它的协议方法

[Objective-C] 纯文本查看 复制代码
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message


如果使用 WKWebview 的话,对于 H5 中的 JavaScript 写法又需要改动为 window.webkit.messageHandlers.<name>.postMessage(<messageBody>)。WKWebView 对于JavaScript 的调用只提供了一种方法:

[Objective-C] 纯文本查看 复制代码
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler


目前项目中考虑到 应用 WKWebview 与 UIWebview + JavaScriptCore 调用方式的差异,暂时放弃了对于 WKWebview的支持。




8、参考

JavaScriptCore

Swift使用JavaScriptCore与JS交互,让Native和HTML5共舞




作者:王瑞华

都看到这里了,就把这篇资料推荐给您的好朋友吧,让他们也感受一下。

回帖是一种美德,也是对楼主发帖的尊重和支持。

*声明:敏捷大拇指是全球最大的Swift开发者社区、苹果粉丝家园、智能移动门户,所载内容仅限于传递更多最新信息,并不意味赞同其观点或证实其描述;内容仅供参考,并非绝对正确的建议。本站不对上述信息的真实性、合法性、完整性做出保证;转载请注明来源并加上本站链接,敏捷大拇指将保留所有法律权益。如有疑问或建议,邮件至marketing@swifthumb.com

*联系:微信公众平台:“swifthumb” / 腾讯微博:@swifthumb / 新浪微博:@swifthumb / 官方QQ一群:343549891(满) / 官方QQ二群:245285613 ,需要报上用户名才会被同意进群,请先注册敏捷大拇指

嗯,不错!期待更多好内容,支持一把:
支持敏捷大拇指,用支付宝支付10.24元 支持敏捷大拇指,用微信支付10.24元

评分

参与人数 1金钱 +20 贡献 +20 专家分 +20 收起 理由
Anewczs + 20 + 20 + 20 32个赞!专家给力!

查看全部评分

我是90后 发表于 2016-10-24 11:40:33 | 显示全部楼层
赞!

不过现在都已经对接JSCore 和 Swift了~
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

做任务,领红包。
我要发帖

分享扩散

都看到这里了,就把这资料推荐给您的好朋友吧,让他们也感受一下。
您的每一位朋友访问此永久链接后,您都将获得相应的金钱积分奖励
热门推荐

合作伙伴

Swift小苹果

  • 北京治世天下科技有限公司
  • ©2014-2016 敏捷大拇指
  • 京ICP备14029482号
  • Powered by Discuz! X3.1 Licensed
  • swifthumb Wechat Code
  •   
快速回复 返回顶部 返回列表