JavaScriptCore 是 JavaScript 的虛擬機(jī),是一個(gè)WebKit的運(yùn)行環(huán)境,為 JavaScript 的執(zhí)行提供底層資源。我們可以利用 JavaScriptCore 中的類(lèi)及協(xié)議,進(jìn)行 OC 與 JS 的交互。
JavaScriptCore中類(lèi)及協(xié)議:
JSContext
給JavaScript提供運(yùn)行的上下文環(huán)境,像是前端開(kāi)發(fā)中的面對(duì)瀏覽器的 window 對(duì)象,代表運(yùn)行時(shí)的一個(gè)全局變量;
通過(guò) -evaluateScript: 方法就可以執(zhí)行一JS代碼,其返回值是JavaScript代碼中最后一個(gè)生成的值;
JSContext的exceptionHandler屬性可用來(lái)接收J(rèn)avaScript中拋出的異常
JSValue
封裝了JS與OC中的對(duì)應(yīng)的類(lèi)型,一個(gè)用來(lái)處理iOS中所有可能存在的JavaScript執(zhí)行后產(chǎn)生Value的類(lèi),可以用來(lái)轉(zhuǎn)換基本數(shù)據(jù)類(lèi)型
JSManagedValue
管理數(shù)據(jù)和方法的類(lèi),包含一個(gè)JSValue對(duì)象,有條件地持有對(duì)象
JSVirtualMachine
完整獨(dú)立的JavaScript執(zhí)行環(huán)境,為JavaScript的執(zhí)行提供底層資源,實(shí)現(xiàn)并發(fā)的JavaScript執(zhí)行,JavaScript和OC橋接對(duì)象的內(nèi)存管理
JSExport
這是一個(gè)協(xié)議,如果采用協(xié)議的方法交互,自己定義的協(xié)議必須遵守此協(xié)議,在協(xié)議中聲明的API都會(huì)在JS中暴露出來(lái),才能調(diào)用
OC調(diào)用JS
JSContext *context = [[JSContext alloc] init];
[context evaluateScript:@"function add(a,b) {return a + b;}"];
//1.取出方法名,調(diào)用
JSValue *add = context[@"add"];
JSValue *sum = [add callWithArguments:@[@7,@8]];
NSLog(@"Sum1: %d", [sum toInt32]);
//2.將調(diào)用過(guò)程繼續(xù)寫(xiě)進(jìn)JSContext里,調(diào)用
JSValue *addValue = [context evaluateScript:@"add(7, 8)"];
NSLog(@"Sum2: %@", addValue.toNumber);
JS調(diào)用OC
1.block方式
JSContext *context = [[JSContext alloc] init];
context[@"log"] = ^(){
NSLog(@"----------Begin log----------");
NSArray *args = [JSContext currentArguments];
for (JSValue *jsVal in args) {
NSLog(@"%@", jsVal);
}
JSValue *this = [JSContext currentThis];
NSLog(@"This: %@", this);
NSLog(@"-----------End log-----------");
};
//web 頁(yè)面可調(diào)用 log 方法,傳入相關(guān)參數(shù),調(diào)用OC端的block,這里直接用 JSContext 執(zhí)行 JS 代碼
[context evaluateScript:@"log('apple',['array1', 'array2'],{key1:'value1',key2:'value2',key3:'value3'});"];
從圖中可以看出,JSValue 對(duì)于 JS 類(lèi)型到 OC 類(lèi)型的處理并不一致,具體如下:
2.使用 webview
首先,在工程中新建一個(gè) Empty 文件,命名為 test.html ,代碼如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>test javascript</title>
</head>
<body>
<div>
<button onclick="zn.a('JS與OC交互');">請(qǐng)看xcode的log</button>
</div>
</body>
</html>
然后,新建一個(gè) model 類(lèi),此類(lèi)必須遵守一個(gè)‘遵守 JSExport 協(xié)議’的協(xié)議,如果想要將一個(gè)自定義類(lèi)的方法暴露給外部的JavaScript使用,那么這個(gè)類(lèi)必須遵守JSExport協(xié)議,JSExport協(xié)議提供了一種聲明式的方法去向JavaScript代碼導(dǎo)出Objective-C的實(shí)例類(lèi)及其實(shí)例方法,類(lèi)方法和屬性:
@protocol PersonJSExport <JSExport>
//將 JS 中的調(diào)用方法名與model類(lèi)中的相對(duì)應(yīng)
JSExportAs(a, - (void)nslog:(NSString *)string);
@end
在model類(lèi)中實(shí)現(xiàn)協(xié)議:
- (void)nslog:(NSString *)string
{
NSLog(@"%@", string);
}
最后,在 controller 中新添一個(gè) webview ,這個(gè) webview 加載剛剛新建的 test.html 中的內(nèi)容:
- (UIWebView *)webView
{
if (_webView == nil) {
_webView = [[UIWebView alloc] initWithFrame:self.view.frame];
NSURL *baseURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
NSString *htmlContent = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"test" ofType:@"html"] encoding:NSUTF8StringEncoding error:nil];
[_webView loadHTMLString:htmlContent baseURL:baseURL];
JSContext *context = [_webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
JsObjCModel *jsOCModel = [JsObjCModel new];
//給js中的對(duì)象賦值
context[@"zn"] = jsOCModel;
}
return _webView;
}
JSContext 是通過(guò) webView 的 valueForKeyPath 獲取的,其路徑為documentView.webView.mainFrame.javaScriptContext;
將一個(gè)初始化好的、可以執(zhí)行協(xié)議中方法的model類(lèi)賦值給 JS 中定義的一個(gè)對(duì)象,在點(diǎn)擊 html 頁(yè)面上的 button 后,JS 執(zhí)行 OC 方法。