javaScriptCore是苹果在iOS7以后推出的一套框架, 一套将原生(OC,Swift均可)和js之间通讯变简单,变强大的框架.
1.1 JSBinding ? Hybrid?
JSBinding是一个技术术语, 通常是这么说的 "JSBinding技术", 那什么是JSBinding技术?
应该拆开看一个是 JS 一个是binding, 字面意思是js绑定/桥接, js绑定/桥接? 绑定/桥接什么?
答:绑定桥接原生.就是js和其他语言的绑定/桥接. 就是js和Native之间建立起了一中绑定/桥接的技术, 使得Native和js之间可以交流的一种技术.
那么我们学的这个javaScriptCore就是一种JSBinding技术,苹果原生提供给我们的.
结构图, 下面橘黄色那一层就是JSBinding技术, JSBinding它打通和js和native之间的鸿沟, 让js和native两者之间能够进行通讯
1.2Hybrid?
Hybrid是web技术和native之间的桥接的技术.
结构图
1.3jsBinding和Hybrid什么关系.
1.3.1首先他两个不是一个东西.
1.3.2第二他两个都可以实现native和js之间 的通讯, jsBinding用的是javaScriptCore而hybrid是通过webVew(我这里仅仅是说iOS,其他平台我不晓得哈.)
1.4JSBinding和Native
// 两者各有各的优点, 那为什么不结合一下了
// 结合
上面的图片来源 大城小胖的视频分享
2.能做什么?
1.我们可以通过javaScriptCore提供的接口,相对简单进行原生(OC,Swift)调用js代码
2.当然我们也可以js调用原生(OC,Swift)功能
~其实就是相互间的调用,在网页上做了一些操作然后想调起手机app原生的某些功能,比如:
①.我们有一个h5的一个商品列表页,然后点击了其中某个商品想进入商品详情页,而商品详情页是一个原生的ViewController,这就涉及的js调用了原生(OC, Swift); ----js->native
②.我们有一个详情页,后台给我们的是一个链接或者html的代码, 其中关于图片显示的问题, 后台设置的css样式显示的图片在我们手机端显示不正常,我们可以用调用js修改图片标签的css的样式,根据我们手机屏幕当前大小做出合适图片显示尺寸. ------native->js
3.过往?
1.没有javaScriptCore之前我们也可以进行原生与js之间的交互.但是必须依赖UIWebView(单纯的就依赖UIWebView着一点来说哈,我个人觉得说不说好坏,因为我觉得,原生和js之间的交互就是通过在h5上发生的一些操作嘛,要展示h5就离不开UIWebView嘛.).
①.js调用原生: 通过实现自己协议URL,在UIWebView的代理,方法
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
根据request拿到请求的url来进行判断,如果url的协议是我自己制定的,那我们就进行js调用原生的相关操作. 比如:~从当前UIWebView控制器,跳转到其他控制器.
②.原生调用js
原生调用js,是通过webView调用其stringByEvaluatingJavaScriptFromString方法发送一个字符串,字符串里面是js代码如下.
NSString * domStr =
@"var headDiv = document.getElementsByClassName('menu')[0];" "var myTitle = ['嘻嘻', '哈哈', '看看', '吱吱'];" "var myIndex = 0;" "for (var i = 0; i < headDiv.childNodes.length; i++){" "if(headDiv.childNodes[i].nodeName != '#text'){" "var myA = headDiv.childNodes[i];" "myA.innerText = myTitle[myIndex];" "myIndex++;" "}" "}" ; [self.webView stringByEvaluatingJavaScriptFromString:domStr];
#import "ViewController.h"
@interface ViewController ()<UIWebViewDelegate>
@property (weak, nonatomic) IBOutlet UIWebView *webView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://m.zongheng.com/"]];
[self.webView loadRequest:request];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
NSString * domStr =
@"var headDiv = document.getElementsByClassName('menu')[0];"
"var myTitle = ['嘻嘻', '哈哈', '看看', '吱吱'];"
"var myIndex = 0;"
"for (var i = 0; i < headDiv.childNodes.length; i++){"
"if(headDiv.childNodes[i].nodeName != '#text'){"
"var myA = headDiv.childNodes[i];"
"myA.innerText = myTitle[myIndex];"
"myIndex++;"
"}"
"}"
;
[webView stringByEvaluatingJavaScriptFromString:domStr];
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSString * str = request.URL.absoluteString;
NSLog(@"%@", str);
// 当点击首页(上面被我们改成了~"嘻嘻")时我们弹出一个VC
NSRange range = [str rangeOfString:@"h5/index?h5=1"];
if (range.location != NSNotFound) {
[self popToViewController];
}
return YES;
}
- (void)popToViewController
{
UIViewController * vc = [[UIViewController alloc]init];
vc.view.backgroundColor = [UIColor purpleColor];
[self presentViewController:vc animated:YES completion:nil];
}
@end
2, javaScriptCore
使用javaScriptCore调用js不需要依赖UIWebView
javaScriptCore里有个JSContext可以直接执行js代码,下面是详细介绍~
#import <JavaScriptCore/JavaScriptCore.h>
// 进入框架头文件我们看见如下
#import "JSContext.h"
#import "JSValue.h"
#import "JSManagedValue.h"
#import "JSVirtualMachine.h"
#import "JSExport.h"
--1--JSContext: JSContext代表JS的执行环境,通过JSContext对象调用evaluateScript方法就可以执行JS代码代. javaScript的所有变量方法都会在ISContext的保存,我们也可通过JSContext对象执行js代码生成新的js函数,js对象变量什么的.当然也可以通过JSContext执行相应的js拿到js里面的变量函数对象什么的.
--2--JSValue 是JSContext执行js代码后得到的结果.JSValue是一个结果,是什么结果? 那就要看看JSContext执行的js代码了. JSValue可以是一个数字, 字符串, 对象, 函数等~当然这些"数字" ,"字符串"都是js类型我们可以通过JSValue的toXXXX, 将这些数据类型转换成OC类型的对应的数据类型.
--- js 和 OC 之间数据类型的转换如下图
图片来源-----iderzheng的博客
暴露给js干嘛?
作用大的去了! 我们把原生的一个对象暴露给js, js就可以调用原生对象里的方法,使用原生对象的属性. 把一个类暴露给js嘛,当然可以使用原生类的类方法喽,同时也可以在js里用原生的类生成原生的对象然后再调用对象的方法. JSExport我觉得真的好厉害的! 后面会详细介绍.
--4--JSManagedValue
NS_CLASS_AVAILABLE(10_9, 7_0)
@interface JSManagedValue : NSObject
js与原生之间的循环引用?
就是原生的对象引用了js里的对象, js对象又对原生的对象引用.
例如说: 我们在Native 里面有的Person这么一个类(这个类必须遵守JSExport协议),这个类生成一个boy对象, 我们将boy这个类塞给jscontext, 然后在js里面用js里的一个girl对象对传进来的这个boy对象进行引用(具体这么引用? 具体就是:girl.boyFriend = boy;), 然后呢我们的boy也引用girl.(你girl对象都把boy对象赋值给了自己boyFriend属性,boy对象必须主动了so:boy.girlFriend = girl;)
但是我们如果向下图一样,互相强引用,倒时候就会造成内存泄漏
所以这样相互间的强引用是错错错滴XXXXXX
上图的代码体现如下------完整代码在下面
- (void)setGirlFriend:(JSValue *)girlFriend { _girlFriend = girlfriend;
}
上面的不行,那就用JSManagedValue, 如图我们用JSManagedValue搞一下,在引用的时候
代码体现如下------完整代码在下面
- (void)setGirlFriend:(JSValue *)girlFriend { _girlFriend = (JSValue *)[JSManagedValue managedValueWithValue:girlFriend];
}
// 关于上面这个哈,我想说明一下哈
// 1.我Person.h里的"girlFriend"这个是下面这个
@property (nonatomic, strong)JSValue * girlFriend;
// 但是
[JSManagedValue managedValueWithValue:girlFriend];
// 上面这个的返回值是JSManagedValue,这样是可以的,到时候你取的时候知道这个属性是JSManagedValue就行了
// 你可能会说干嘛外面不直接这样
@property (nonatomic, strong) JSManagedValue * girlFriend;
// 这样是不行的,因为从js那边传过来的是JSValue,如果我这样,因为穿过来的是JSValue,而我们的是 JSManagedValue, 所以js那边赋值时根本进不了set方法
// js那边根本进不来
- (void)setGirlFriend:(JSManagedValue *)girlFriend
{}
// 当然你也在JSExport里面不是暴露如下的这种属性
@property (nonatomic, strong)JSValue * girlFriend;
// 你可以暴露一个接受一个JSValue 参数的方法, 然后让js那边调用这个方法把girl传过来
// 点击查看视频资料
上面的三个图和关于相互之间引用的例子思想,来源一个大神视频分享
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<p></p>
<div id="myDiv" style="background-color: red; width: 200px; height: 22px;"></div>
<button id="myBtn2">加属性用的</button>
<button id="mybtn">点我啊笨蛋</button>
<!---->
<script type="text/javascript"> var boy; var btn = document.getElementById('mybtn'); var div = document.getElementById('myDiv'); var btn2 = document.getElementById('myBtn2'); function JsGirl(){ this.boyFriend = ""; this.name = "beauty girl"; this.changeDivColor = function(){ div.style.backgroundColor = "#333"; } } var jsGirl = new JsGirl(); btn2.onclick = function(){ alert("进来了!"); jsGirl.boyFriend = boy; jsGirl.boyFriend.nickName = "pig"; boy.girlFriend = jsGirl; } btn.onclick = function(){ console.log("你还真点啊!"); alert("你还真点啊!"); //boy.seyHello('xixi'); //boy.seyHi([90,23,"22",{"key":"99"}]); // boy.gotoVcWithTitle("detailVcType", "I miss You!"); jsGirl.boyFriend = boy; boy.girlFriend = jsGirl; boy.girlFriend.changeDivColor(); jsGirl.boyFriend.pay(); // boy.gotoAVc("213123", "hi meirenji"); // boy = NULL; // boy.seyClassMethod(); //var testPerson = boy.myAllocInit(); //var testPerson = boy.alloc().init(); //testPerson.gotoAVc("213123", "hi meirenji"); //alert(testPerson); // testPerson.gotoAVc("213123", "hi meirenji"); } //btn.onclick = tt.seyHello(); </script>
</body>
</html>
person.h
//
// Person.h
// javaScriptCoreWebView
//
// Created by Mac on 16/7/25.
// Copyright © 2016年 wutong. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>
@class Person;
@protocol PersonProtocol <JSExport>
- (void)pay;
- (void)seyHello;
- (void)seyHi:(NSArray *)arr;
- (void)gotoVcWith:(NSString *)typeStr title:(NSString *)titleStr;
+ (Person *)myAllocInit;
JSExportAs(gotoAVc,
- (void)gotoActivityVcWithId:(NSString *)aId title:(NSString *)titleStr
);
+ (void)seyClassMethod;
@property (nonatomic, strong)JSValue * girlFriend;
@property (nonatomic, strong)NSString * nickName;
//+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
//- (instancetype)init;
@end
@interface Person : NSObject<PersonProtocol>
@property (nonatomic, strong)NSString * nickName;
@property (nonatomic, strong)JSValue * girlFriend;
- (void)pay;
- (void)seyHello;
- (void)seyHi:(NSArray *)arr;
- (void)gotoVcWith:(NSString *)typeStr title:(NSString *)titleStr;
- (void)gotoActivityVcWithId:(NSString *)aId title:(NSString *)titleStr;
+ (void)seyClassMethod;
+ (Person *)myAllocInit;
@end
//
// Person.m
// javaScriptCoreWebView
//
// Created by Mac on 16/7/25.
// Copyright © 2016年 wutong. All rights reserved.
//
#import "Person.h"
@implementation Person
- (void)setGirlFriend:(JSValue *)girlFriend
{
_girlFriend = (JSValue *)[JSManagedValue managedValueWithValue:girlFriend];
}
- (void)pay
{
NSLog(@"付钱!!!");
}
- (void)setNickName:(NSString *)nickName
{
_nickName = nickName;
}
//- (void)setNickName:(NSString *)nickName
//{
//}
- (void)seyHello
{
NSLog(@"------helloh");
}
- (void)seyHi:(NSArray *)arr
{
NSArray * testArr = [NSArray array];
NSMutableArray * testMArr = [NSMutableArray array];
NSLog(@"%@----M:%@", NSStringFromClass([testArr class]), NSStringFromClass([testMArr class]));
NSLog(@"----%@", NSStringFromClass([arr class]));
NSLog(@"hi-----%@", arr);
}
- (void)gotoVcWith:(NSString *)typeStr title:(NSString *)titleStr
{
NSLog(@"typeStr:%@ -----titleStr:%@", typeStr, titleStr);
}
- (void)gotoActivityVcWithId:(NSString *)aId title:(NSString *)titleStr
{
NSLog(@"活动id:%@-----活动title:%@", aId, titleStr);
}
+ (Person *)myAllocInit
{
return [[Person alloc]init];
}
+ (void)seyClassMethod
{
NSLog(@"hi, 我是类方法哦~");
}
- (void)dealloc
{
NSLog(@"person-----挂了!");
}
@end
//
// ViewController.m
// javaScriptCoreWebView
//
// Created by Mac on 16/7/25.
// Copyright © 2016年 wutong. All rights reserved.
//
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()<UIWebViewDelegate>
@property (weak, nonatomic) IBOutlet UIWebView *webView;
@property (nonatomic, strong)JSManagedValue * jsMValue;
@property (nonatomic, strong)JSContext * jsContext;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSURL * jsFileUrl = [[NSBundle mainBundle] URLForResource:@"jsOCTest.html" withExtension:nil];
NSURLRequest * request = [NSURLRequest requestWithURL:jsFileUrl];
Person * p = [[Person alloc]init];
[self.webView loadRequest:request];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
Person *jsObject = [[Person alloc]init];
//self.jsContext[@"Person"] = [Person class];
// self.jsContext[@"boy"] = jsObject; //将对象注入这个context中
// JSValue * value = (JSValue *)jsObject;
self.jsContext[@"boy"] = jsObject;
//JSValue * valueM = [self.jsContext evaluateScript:@"boy"];
//
// _jsMValue = [JSManagedValue managedValueWithValue:valueM];
// [self.jsContext.virtualMachine addManagedReference:_jsMValue withOwner:self];
}
- (IBAction)callJsGrilDo:(id)sender {
JSValue * pJsValue = self.jsContext[@"boy"];
Person * person = [pJsValue toObject];
JSManagedValue * jsMa = person.girlFriend;
JSValue * jsV = jsMa.value;
JSValue * kk = jsV[@"changeDivColor"];
[kk callWithArguments:nil];
}
-(void)dealloc{
NSLog(@"VC-----挂了!!!");
if(_jsContext){
// [_jsContext.virtualMachine removeManagedReference:_jsMValue withOwner:self];
_jsContext[@"boy"] = nil;
_jsContext = nil;
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
Person *jsObject = [[Person alloc]init];
self.jsContext[@"boy"] = jsObject;
}
iderzheng的博客
不同内存管理机制—Reference Counting vs. Garbage Collection
虽然Objetive-C和JavaScript都是面向对象的语言,而且它们都可以让程序员专心于业务逻辑,不用担心内存回收的问题。但是两者的内存回首机制全是不同的,Objective-C是基于引用计数,之后Xcode编译器又支持了自动引用计数(ARC, Automatic Reference Counting);JavaScript则如同Java/C#那样用的是垃圾回收机制(GC, Garbage Collection)。当两种不同的内存回收机制在同一个程序中被使用时就难免会产生冲突。
比如,在一个方法中创建了一个临时的Objective-C对象,然后将其加入到JSContext放在JavaScript中的变量中被使用。因为JavaScript中的变量有引用所以不会被释放回收,但是Objective-C上的对象可能在方法调用结束后,引用计数变0而被回收内存,因此JavaScript层面也会造成错误访问。
同样的,如果用JSContext创建了对象或者数组,返回JSValue到Objective-C,即使把JSValue变量retain下,但可能因为JavaScript中因为变量没有了引用而被释放内存,那么对应的JSValue也没有用了。
怎么在两种内存回收机制中处理好对象内存就成了问题。JavaScriptCore提供了JSManagedValue类型帮助开发人员更好地管理对象内存。
demo
--5--JSVirtualMachine
相互调用主要说的是js调用原生, 原生调用js
我们不需要依赖webView,这一部分简单的来说,有JSValue和JSContext就行了.
// VC里基本的代码
#import "ViewController.h"
#import <JavaScriptCore/JavaScriptCore.h>
@interface ViewController ()
@property (nonatomic, strong)JSContext * jsContext;
@end
@implementation ViewController
- (JSContext *)jsContext{
if (!_jsContext) {
_jsContext = [[JSContext alloc]init];
}
return _jsContext;
}
- (void)viewDidLoad {
[super viewDidLoad];
}
@end
- (void)nativeCallJS_1
{
JSValue * resultValue = [self.jsContext evaluateScript:@"1 + 2"];
int32_t result = [resultValue toInt32];
result--;
NSLog(@"%zd", result); // 2
}
数字加字符串等于字符串
- (void)nativeCallJS_1
{
JSValue * resultValue = [self.jsContext evaluateScript:@"1 + '2' "];
NSString * result = [resultValue toString];
NSLog(@"%@", result); // @"12"
NSLog(@"%d", result.length); // 2
}
// 调用js函数,返回普通的结果
- (void)nativeCallJS_func1
{
// 给js注入一个函数test1, 让后调用test1
JSValue * jsFunctionResult = [self.jsContext evaluateScript:
@"function test1(){"
"return 'xixi';"
"}"
"test1();"];
// js函数返回的是字符串 'xixi',我们将结果用NSString接收
NSString * result = [jsFunctionResult toString];
NSLog(@"%@", result); // xixi
}
// 调用js函数, 返回函数 ---------------------------闭包1
- (void)nativeCallJS_func2
{
// 给js注入一个函数test1, 然后返回的是这个函数----闭包
JSValue * jsFunctionResult = [self.jsContext evaluateScript:
@"(function test1(){"
"return 'xixi';"
"})"
];
// 返回的是一个函数, 我们调用这个函数然后拿到函数的返回结果xixi
// callWithArguments 后面要一个数组,这个数组是说test1如果需要参数我们调用时,参数就是放在数组中,后面会说~
JSValue * jsFunctionStringReasult = [jsFunctionResult callWithArguments:nil];
NSString * result = [jsFunctionStringReasult toString];
NSLog(@"%@", result);
}
// 调用js函数, 返回函数 ---------------------------闭包2
- (void)nativeCallJS_func3
{
// 执行一个闭包
JSValue * jsFunctionResult = [self.jsContext evaluateScript:
@"function block3(){"
"var xixi = '(*^__^*) 嘻嘻……';"
"var lala = '♪(^∇^*)';"
"function block4(){"
"return xixi + lala;"
"}"
"return block4;"
"}"
"block3();"];
JSValue * jsFunctionStringReasult = [jsFunctionResult callWithArguments:nil];
NSString * result = [jsFunctionStringReasult toString];
NSLog(@"%@", result);//(*^__^*) 嘻嘻……♪(^∇^*)
}
// 调用js函数---------------------------文件执行
//工程里新建一个test.js文件
// test.js文件里面代码如下
function blockSum(a,b){
var num = 99;
function blockS(){
return a+b+num;
}
return blockS;
}
blockSum(5,6)();
// 调用
- (void)nativeCallJS_func4
{
// 执行一个文件
NSString *path = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"js"];
NSString *jsFileStr = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
JSValue * jsValue3 = [self.jsContext evaluateScript:jsFileStr];
int32_t result = [jsValue3 toInt32];
NSLog(@"%d", result); // 110
}
// 调用js函数,--------------------------- 先注册后调用
- (void)nativeCallJS_func5
{
// 注册一个js然后执行
// 1.变量接收函数
[self.jsContext evaluateScript:
@"var hellow = function(){"
"return 'hello';"
"}"
];
// 2.函数名
[self.jsContext evaluateScript:
@"function hi(){"
"return 'hi';"
"}"
];
// 3.闭包函数体内
[self.jsContext evaluateScript:
@"function hiBlock(){"
"var myHi = 'hi block';"
"function sayHi(){"
"return myHi;"
"}"
"return sayHi;"
"}"
];
// // 4.闭包匿名?
JSValue *jsFunction = [self.jsContext evaluateScript:
@"(function(){"
"return 'I am block too';"
"})"
];
JSValue * jsValueHello = [self.jsContext evaluateScript:@"hellow();"];
NSLog(@"%@", jsValueHello);
JSValue * jsValueHi = [self.jsContext evaluateScript:@"hi();"];
NSLog(@"%@", jsValueHi);
JSValue * jsValueSayHi = [self.jsContext evaluateScript:@"hiBlock()();"];
NSLog(@"%@", jsValueSayHi);
JSValue * jsFunctionValue = [jsFunction callWithArguments:nil];
NSLog(@"%@", jsFunctionValue);
}
// 传值几种情况
- (void)nativeCallJS_func6
{
JSValue * sum = [self.jsContext evaluateScript:
@"(function sum(a, b){"
"return a + b;"
"})"
];
JSValue * result = [sum callWithArguments:@[@11, @19]];
NSLog(@"%@",result);
[self.jsContext evaluateScript:
@"function getInfo(){"
"var name = '小明';"
"function info(addr){"
"return name + '家住' + addr;"
"}"
"return info;"
"}"
];
JSValue * block2Result = [self.jsContext[@"getInfo"] callWithArguments:nil];
JSValue * block2Result2 = [block2Result callWithArguments:@[@"北京"]];
NSLog(@"%@", block2Result2);
[self.jsContext evaluateScript:
@"function sumS(a, b){"
"return a + b;"
"}"
];
JSValue * result2 = [self.jsContext evaluateScript:@"sumS(33,67);"];
NSLog(@"%@",result2);
}
// 操作数组
- (void)nativeCallJS_value
{
// 创建数组修改数组获得数组
//1. 拿到js中的数组
[self.jsContext evaluateScript:@"var arr = ['哈哈哈', '(*^__^*) 嘻嘻……'];"];
JSValue * arrValue = self.jsContext[@"arr"];
NSArray * arr = [arrValue toArray];
NSLog(@"%@",arr);
// 2.修改数组
arrValue[1] = @"O(∩_∩)O哈哈哈~";
arrValue[5] = @123; // js中没有下标5的. 没有的用空补齐
NSLog(@"%@",arrValue);// 打印: 哈哈哈,O(∩_∩)O哈哈哈~,,,,123
NSArray * arrChange = [arrValue toArray];
NSLog(@"%@",arrChange);
/* 哈哈哈, O(∩_∩)O哈哈哈~, <null>, <null>, <null>, 123 ) 2,3,4下标以前不存在, 所用null代替 */
}
// 操作js对象
// 获得对象, 调用对象方法
- (void)nativeCallJS_value_Obj
{
[self.jsContext evaluateScript:
@"function myObject(){"
"var name = 'wutong';"
"this.mylog = function(){"
"return name;"
"}"
"}"
"var myOj1 = new myObject();"
];
// 拿现成的
// JSValue * value1 = [self.jsContext evaluateScript:@"myOj1.mylog()"];
// NSLog(@"%@", value1);
// JSValue * value2 = [value1 callWithArguments:nil];
// NSLog(@"---%@", value2);
// 自己创建一个对象
JSValue * value3 = [self.jsContext evaluateScript:@"(function(){var myOj2 = new myObject(); return myOj2;})()"];
JSValue * value4 = [value3[@"mylog"] callWithArguments:nil];
NSLog(@"--value4:%@", value4);
}
// block传入js, 当做js的函数(方法)调用
/ / 调用js方法然后回调到oc中, 虽然是在oc里面但是js方法里面的特性,比如参数不固定,在回调的block中依然是保留的.
// 使用JSContext 的+ (NSArray *)currentArguments方法获取传进来的所有参数
解释文案来之--iderzheng博客
- (void)jSCallNative
{
// 在下面这个block中绝对不能使用self.jsContext
self.jsContext[@"sum"] = ^(){
// self.jsContext...... 使用外部定义好的Context绝对不行,如果一定要使用Context,如下
//
// [JSContext currentContext]; /如果要使用就这样获取然后再使用
NSArray * args = [JSContext currentArguments];
int32_t result = 0;
for (JSValue *jsVal in args) {
NSLog(@"%@",jsVal);
result = result + [jsVal toInt32];
}
NSLog(@"result: %d", result);
};
[self.jsContext evaluateScript:@"sum(1,2,4,3)"];
}
JSExport 上面说了是一个协议嘛,这个协议是原生和js之间互通的.本身这个协议里面没有约定什么方法.
点进去看,果然毛都没有,除了一大段的注释
@protocol JSExport
@end
我们实现一个继承了JSExport协议的协议,这个协议里的方法就可以在js里面调用.废话不多说看看我的Person
//
// Person.h
// javaScriptCoreWebView
//
// Created by Mac on 16/7/25.
// Copyright © 2016年 wutong. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>
@class Person;
@protocol PersonProtocol <JSExport>
- (void)pay;
- (void)seyHello;
- (void)seyHi:(NSArray *)arr;
- (void)gotoVcWith:(NSString *)typeStr title:(NSString *)titleStr;
+ (Person *)myAllocInit;
JSExportAs(gotoAVc,
- (void)gotoActivityVcWithId:(NSString *)aId title:(NSString *)titleStr
);
+ (void)seyClassMethod;
@property (nonatomic, strong)JSValue * girlFriend;
@property (nonatomic, strong)NSString * nickName;
//+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
//- (instancetype)init;
@end
@interface Person : NSObject<PersonProtocol>
@property (nonatomic, strong)NSString * nickName;
@property (nonatomic, strong)JSValue * girlFriend;
- (void)pay;
- (void)seyHello;
- (void)seyHi:(NSArray *)arr;
- (void)gotoVcWith:(NSString *)typeStr title:(NSString *)titleStr;
- (void)gotoActivityVcWithId:(NSString *)aId title:(NSString *)titleStr;
+ (void)seyClassMethod;
+ (Person *)myAllocInit;
@end
恩恩~那啥,代码从上往下看哈,
</>
我想对上面的6点挑几个详细说一下
// 1.把Person类的对象塞给JSContext上下文
// 这一点是最基础的,不把圆的对象给js, 要js怎么调用原生的嘛,代码如下
// 我这里是结合了webView 来说的, 所以我在在加载完成页面的时候把boy对象给webView的上下文
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
Person *boy = [[Person alloc]init];
self.jsContext[@"boy"] =boy;
}
把对象boy给了js后,就可以在js那边调用原生的东西了
// 2.调用没有boy对象方法---------没有参数
// 我不把全部代码提出来了哈, 但我说明一下boy在js里全局变量,触发js调用是我们点击html 上面的btn. , btn我们通过dom方式已经拿到了,具体看上面html的代码哈, 下面的话我就直接用了不解释了哈.
<script type="text/javascript"> btn.onclick = function(){ console.log("你还真点啊!"); alert("你还真点啊!"); boy.seyHello(); } </script>
// 3.调用没有boy对象方法---------一个参数
<script type="text/javascript"> btn.onclick = function(){ console.log("你还真点啊!"); alert("你还真点啊!"); var arr = ['xixi', 'haha']; boy.seyHi(arr); } </script>
// 4.调用没有boy对象方法---------多个参数
- (void)gotoVcWithTypeStr:(NSString *)typeStr title:(NSString *)titleStr;
- (void)gotoVcWithId:(NSString *)idStr type:(NSString *)typeStr title:(NSString *)titleStr;
// 上面两个多参方法要想在js里用boy对象调用应该修改为如下
gotoVcWithTypeStrTitle
gotoVcWithIdTypeTitle
去掉":(XXX *)XXX" 让后后面的方法名拼过来第一个字母大写
那么当boy调用就是如下
boy.gotoVcWithTypeStrTitle("typeStr", "titleStr");
boy.gotoVcWithIdTypeTitle("13231", "typeStr", "titleStr");/ /第一个是id,第二个是type,第三个是title
<script type="text/javascript"> btn.onclick = function(){ console.log("你还真点啊!"); alert("你还真点啊!"); boy.gotoVcWithTitle("typeStr", "titleStr"); } </script>
// 其实如果你的参数多或者你赖的修改方法, 你可以给boy对象的多参方法,取一个绰号,然后在js用方法的绰号调用也是一样的,只要你参数顺序要对,一一对应.
// 在PersonProtocol协议里取绰号(别名)
@protocol PersonProtocol <JSExport>
- (void)gotoVcWith:(NSString *)typeStr title:(NSString *)titleStr;
JSExportAs(gotoAVc,
- (void)gotoActivityVcWithId:(NSString *)aId title:(NSString *)titleStr
);
@end
<script type="text/javascript"> btn.onclick = function(){ console.log("你还真点啊!"); alert("你还真点啊!"); //boy.gotoVcWithTitle("typeStr", "titleStr"); boy. gotoAVc("typeStr", "titleStr");// 和上面是一个意思 } </script>
// 5.调用Person类------注册Person类
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
self.jsContext[@"Person"] = [Person class];
}
// 6.调用Person类------调用类方法
多参什么的和上面的规则一样
<script type="text/javascript"> btn.onclick = function(){ console.log("你还真点啊!"); alert("你还真点啊!"); Person.seyClassMethod(); } </script>
// 7.调用Person类------在js里面创建对象
// 一般在js 里面创建对象是用构造函数,相当于iOS的类嘛, 我们刚才已经注册了一个Person类了(相当于构造函数),我们是不就可以像js那样new一个对象出来了.像下面这个
var student = new Person();
// 答案是否定!!!!!
/ / 我们这边是注册了一个类给js,但是js那边如果我们打印实际上Person还是一个对象,而不是一个函数, 说以如果我们需要在js里面创建Person的对象,我们需要下面两个步奏
@protocol PersonProtocol <JSExport>
+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
- (instancetype)init;
@end
var testPerson = Person.alloc().init();
testPerson.gotoAVc("213123", "hi meirenji");
alert(testPerson);
testPerson.gotoAVc("213123", "hi meirenji");
在js执行时如果遇到错误,会被JSContext捕获,并会存储在exception属性上,但是错误并不会向外面抛出. 我们可以设置JSContext对象的exceptionHandler. 是一个block
@property (copy) void(^exceptionHandler)(JSContext *context, JSValue *exception);
这样每当发生错误被JSContext捕获,然后赋值时,我们就可以打印一下如下
self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception) {
NSLog(@"%@", exception);
context.exception = exception;
};
// 错误测试
[self.jsContext evaluateScript:@"nibaba();"];
本文参考
iOS7新JavaScriptCore框架入门介绍
JavaScriptCore初探
iOS开发:JavaScriptCore.framework的简单使用--JS与OC的交互篇
大城小胖的视频分享----JSBinding、ios7_and_JavaScriptCore
native调用js看
javaScriptCoreDemo
js调用Native看
javaScriptCoreDemo-WebView