JavaScriptCore

从应用程序内部评估JavaScript(JavaScript(JS)是一种基于对象的脚本语言,它不仅可以创建对象,也能使用现有的对象。JavaScript 是属于 HTML 和 Web 的编程语言。)程序,并支持应用程序的JavaScript脚本。

概述

JavaScriptCore框架提供了从Swift、Objective-C和基于c的应用程序中评估JavaScript程序的能力。您还可以使用JavaScriptCore向JavaScript环境插入定制对象。

1.JSContext JSContext对象表示JavaScript执行环境。您创建并使用JavaScript上下文来评估来自Objective-C或Swift代码的JavaScript脚本,访问在JavaScript中定义或计算的值,以及使JavaScript可以访问本机对象、方法或函数。

2.JSManagedValue JSManagedValue对象封装JSValue对象,添加“条件保留”行为来提供值的自动内存管理。托管值的主要用例是将JavaScript值存储在Objective-C或Swift对象中,该对象本身被导出到JavaScript。

3.JSValue JSValue实例是对JavaScript值的引用。您可以使用JSValue类在JavaScript和Objective-C或Swift表示之间转换基本值(比如数字和字符串),以便在本机代码和JavaScript代码之间传递数据。您还可以使用这个类来创建JavaScript对象,这些对象包装自定义类或JavaScript函数的本机对象,其实现由本机方法或块提供。

4.JSVirtualMachine JSVirtualMachine实例表示用于JavaScript执行的自包含环境。使用该类有两个主要目的:支持并发JavaScript执行,以及管理连接在JavaScript和Objective-C或Swift之间的对象的内存。

协议-Protocols---JSExport 将Objective-C类及其实例方法、类方法和属性导出到JavaScript代码的协议。

JSBase.h 定义JavaScriptCore接口引擎。

JSContextRef.h JSObjectRef.h 一个JavaScript对象。

JSStringRef.h 一个UTF16字符缓冲区,它是JavaScript中的基本字符串表示。

JSStringRefCF.h 包含CFString便利方法。

JSValueRef.h 一个JavaScript值,它是所有JavaScript值及其上的多态函数的基类型。

JSContext

SContext对象表示JavaScript执行环境。您创建并使用JavaScript上下文来评估来自Objective-C或Swift代码的JavaScript脚本,访问在JavaScript中定义或计算的值,以及使JavaScript可以访问本机对象、方法或函数。

创造JSContext对象

  • init 初始化一个新的JavaScript上下文。

  • initWithVirtualMachine: 创建与特定虚拟机关联的新JavaScript上下文。

评估脚本

  • evaluateScript: 执行指定的JavaScript代码。

  • evaluateScript:withSourceURL: 执行指定的JavaScript代码,将指定的URL视为其源位置。

检查正在运行的互斥量中的回调状态

  • currentContext 返回当前执行JavaScript代码的上下文。

  • currentCallee 返回当前执行的JavaScript函数。

  • currentThis 返回当前执行JavaScript代码中此关键字的值。

  • currentArguments 从JavaScript代码返回当前本机回调的参数。

使用JavaScript全局状态

globalObject 与上下文关联的JavaScript全局对象。

exception 在脚本的计算中抛出一个JavaScript异常。

exceptionHandler 要调用的块应该在抛出JavaScript异常时评估脚本结果。

virtualMachine 上下文所属的JavaScript虚拟机。

name 上下文的描述性名称。

使用下标访问JavaScript全局状态

  • objectForKeyedSubscript:

返回上下文的全局对象中指定的JavaScript属性的值,允许使用下标getter语法。

  • setObject:forKeyedSubscript:

设置上下文的全局对象的指定JavaScript属性,允许使用下标setter语法。

使用C JavaScriptCore API

JSGlobalContextRef 返回JavaScript上下文的C表示形式。

  • contextWithJSGlobalContextRef: 从等价的C表示创建JavaScript上下文对象。

JSManagedValue

JSManagedValue对象封装JSValue对象,添加“条件保留”行为来提供值的自动内存管理。托管值的主要用例是将JavaScript值存储在Objective-C或Swift对象中,该对象本身被导出到JavaScript。

重要的

不要在导出到JavaScript的本机对象中存储非托管JSValue对象。因为JSValue对象引用它所包含的JSContext对象,所以这个操作创建了一个retain循环,从而避免释放上下文。

托管值的“条件保留”行为确保只要以下条件为真,托管值的底层JavaScript值就会被保留:

JavaScript值可以通过JavaScript对象图访问(即不受JavaScript垃圾收集的影响)

JSManagedValue对象可以通过Objective-C或Swift对象图访问,正如使用addManagedReference(_:withOwner:)方法向JavaScriptCore虚拟机报告的那样

但是,如果这两个条件都不为真,则托管值将其value属性设置为nil,释放底层JSValue对象。

创造Managed Value

  • initWithValue:使用指定的JavaScript值初始化托管值。
  • managedValueWithValue:使用指定的JavaScript值创建托管值。

  • managedValueWithValue:andOwner:创建托管值并将其与所有者关联。

访问Managed Value

value---托管值的底层JavaScript值。

JSValue

JSValue实例是对JavaScript值的引用。您可以使用JSValue类在JavaScript和Objective-C或Swift表示之间转换基本值(比如数字和字符串),以便在本机代码和JavaScript代码之间传递数据。您还可以使用这个类来创建JavaScript对象,这些对象包装自定义类或JavaScript函数的本机对象,其实现由本机方法或块提供。

每个JSValue实例都源自一个JSContext对象,该对象表示包含该值的JavaScript执行环境。该值持有对其上下文对象的强引用——只要保留与特定JSContext实例相关联的任何值,该上下文就仍然是活动的。当在JSValue对象上调用实例方法,并且该方法返回另一个JSValue对象时,返回的值属于与原始值相同的上下文。

每个JavaScript值还与一个特定的JSVirtualMachine对象关联(通过上下文属性间接关联),该对象表示其上下文的底层执行资源集。您只能将JSValue实例传递给同一虚拟机承载的JSValue和JSContext实例上的方法,试图将一个值传递给另一个虚拟机会引发Objective-C异常。

在JavaScript和本机类型之间进行转换

当您使用以下方法创建JavaScript值、读取和转换JavaScript值时,JavaScriptCore会自动将本机值转换为JavaScript值,反之亦然,使用下面总结的规则。

1.NSDictionary对象或Swift字典及其包含的键将成为具有匹配的命名属性的JavaScript对象,反之亦然。键值是递归复制和转换的。

2.NSArray对象或Swift数组变成JavaScript数组,反之亦然,元素递归地复制和转换。

3.Objective-C块(或带有@convention(block)属性的Swift闭包)变成JavaScript函数对象,参数和返回类型使用与值相同的规则转换。转换由本机块或方法支持的JavaScript函数返回该块或方法;所有其他JavaScript函数都转换为空字典。

  1. 对于所有其他本机对象类型(以及类类型或元类型),JavaScriptCore创建一个JavaScript包装器对象,其中包含一个反映本机类层次结构的构造函数原型链。默认情况下,原生对象的JavaScript包装器不会使该对象的属性和方法在JavaScript中可用。

JSExport---将Objective-C类及其实例方法、类方法和属性导出到JavaScript代码的协议。

将Objective-C对象导出到JavaScript

当您从Objective-C类的实例创建JavaScript值,而JSValue类没有指定复制约定时,JavaScriptCore创建一个JavaScript包装器对象。(对于某些类,JavaScriptCore会自动将值复制到适当的JavaScript类型;例如,NSString实例变成JavaScript字符串。)

在JavaScript中,通过原型对象链支持继承。对于导出的每个Objective-C类,JavaScriptCore都在包含的JavaScript上下文(JSContext对象)中创建一个原型。对于NSObject类,prototype对象是JavaScript上下文的对象原型。对于所有其他Objective-C类,JavaScriptCore创建了一个prototype对象,它的内部[prototype]属性指向为Objective-C类的超类创建的prototype属性。因此,JavaScript包装器对象的原型链反映了被包装的Objective-C类型的继承层次结构。

除了原型对象之外,JavaScriptCore还为每个Objective-C类生成一个JavaScript构造函数对象。

将Objective-C方法和属性暴露给JavaScript

默认情况下,Objective-C类的任何方法或属性都不会暴露给JavaScript;相反,您必须选择要导出的方法和属性。对于类所遵循的每个协议,如果协议包含JSExport协议,那么JavaScriptCore将该协议解释为要导出到JavaScript的方法和属性列表。

对于导出的每个实例方法,JavaScriptCore都创建一个对应的JavaScript函数作为原型对象的属性。对于每个导出的Objective-C属性,JavaScriptCore都会在原型上创建一个JavaScript访问器属性。对于导出的每个类方法,JavaScriptCore在构造函数对象上创建一个JavaScript函数。例如,清单1和清单2演示了JSExport协议的采用,以及用JavaScript导出的类所表示的API。

实战------OC和JavaScript交互

test.html

<!DOCTYPE html>

<html>

    <meta charset="UTF-8">

    <div style="margin-top: 100px">

        <h1>Objective-C和JavaScript交互</h1>

        <input type="button" value="CallCamera" onclick="jsObj.callCamera()">

            </div>

    <div>

        <input type="button" value="Share" onclick="callShare()">

            </div>

    <script>

        varcallShare =function() {

            varshareInfo = JSON.stringify({"title":"标题","desc":"内容", });

            jsObj.share(shareInfo);//调用OC方法,含参数的

        }

    varpicCallback =function(photos) {

      //picCallback--oc的回调方法  photos回调参数

        alert(photos);

    }

    varshareCallback =function(){

        alert('success1');

    }

</html>

-----------------------JSModel.h-------------------

import <Foundation/Foundation.h>

import<JavaScriptCore/JavaScriptCore.h>

import<UIKit/UIKit.h>

@protocol JSExportDelegate <JSExport>//JSExportDelegate要继承JSExport

  • (void)callCamera;

  • (void)share:(NSString*)shareString;

// 在JS中调用时,函数名应该为showAlertMsg(arg1, arg2)

// 这里是只两个参数的。

  • (void)showAlert:(NSString)title msg:(NSString)msg;

@end

@interface JSModel : NSObject<JSExportDelegate>

@property (nonatomic, strong) JSContext *jsContext;

@property (nonatomic, strong) UIWebView *webView;

@end

-----------------------JSModel.m------------------

import "JSModel.h"

@implementation JSModel

pragma mark---协议JSExportDelegate

  • (void)callCamera{

    NSLog(@"callCamera");

    // 获取到照片之后在回调js的方法picCallback把图片传出去

    JSValue *picCallback =self.jsContext[@"picCallback"];//picCallback回调的方法,picCallback(H5的方法名)

    [picCallback callWithArguments:@[@"photos"]];//返回的参数

}

  • (void)share:(NSString *)shareString{

    NSLog(@"share:%@", shareString);

    // 分享成功回调js的方法shareCallback---最好在子线程调用

    JSValue *shareCallback =self.jsContext[@"shareCallback"];

    [shareCallback callWithArguments:nil];

}

  • (void)showAlert:(NSString *)title msg:(NSString *)msg{

}

@end

---------------------JSViewController.h--------------------------

import <UIKit/UIKit.h>

@interfaceJSViewController :UIViewController

@end

---------------------JSViewController.m-------------------------

import "JSViewController.h"

import<JavaScriptCore/JavaScriptCore.h>

import "JSModel.h"

@interface JSViewController ()<UIWebViewDelegate>

@property (nonatomic, strong) JSContext *jsContext;

@property (nonatomic, strong) UIWebView *mywebView;

@end

@implementationJSViewController

  • (void)viewDidLoad {

    [super viewDidLoad];

[self initInterface];

[self initJavaScript];

}

pragma mark-----脚本

  • (void)initJavaScript{
self.mywebView = [[UIWebView alloc]initWithFrame:CGRectMake(0, 6, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)];

self.mywebView.delegate =self;

NSURL* url= [NSURL URLWithString:[[NSBundle mainBundle]pathForResource:@"test" ofType:@"html"]];

[self.mywebView loadRequest:[[NSURLRequest alloc]initWithURL:url]];

[self.view addSubview:self.mywebView];

}

pragma mark--- UIWebViewDelegate

  • (void)webViewDidFinishLoad:(UIWebView*)webView {
/*

 关于交互对象的注入时机,在- (void)webViewDidFinishLoad:(UIWebView *)webView中去注入交互对象,但是这时候网页还没加载完,JavaScript那边已经调用交互方法,这样就会调不到原生应用的方法而出现问题。如果在创造一个UIWebView的子类,每个交互都在子类进行,在- (void)viewDidLoad中去注入交互对象,这样倒是解决了上面的问题,但是同时又引起了一个新的问题就是在一个网页内部点击链接跳转到另一个网页的时候,第二个页面需要交互,这时JSContext环境已经变化,但是- (void)viewDidLoad仅仅加载一次,跳转的时候,没有再次注入交互对象,这样就会导致第二个页面没法进行交互。所以针对这样的问题我的想法是在子类的 (void)viewDidLoad中添加一个定时器,每隔一定时间就注入一次交互对象。

 */

self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

JSModel*model = [[JSModelalloc]init];

model.jsContext =self.jsContext;

model.webView = self.mywebView;

self.jsContext[@"jsObj"] = model ;// //注入JS代码,定义JS中需要调用的jsObj    self.jsContext[@"jsObj"] = self 注入的交互对象为控制器self,这样JSContext环境引用控制器self,在退出控制器的时候,因为控制器self被JSContext引用而不释放,而JSContext只有等控制器释放了才能随之释放,所以就引起了循环引用,造成内存泄露。

self.jsContext.exceptionHandler= ^(JSContext*context,JSValue*exceptionValue) {

    context.exception= exceptionValue;

    NSLog(@"异常信息:%@", exceptionValue);

};

}

pragma mark---界面初始化

  • (void)initInterface{
self.title=@"JS";

self.view.backgroundColor = [UIColor whiteColor];

}

@end

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容