JavaScriptCore 是 JavaScript 的虛擬機(jī)今阳,是一個(gè)WebKit的運(yùn)行環(huán)境绿语,為 JavaScript 的執(zhí)行提供底層資源写隶。我們可以利用 JavaScriptCore 中的類及協(xié)議赡盘,進(jìn)行 OC 與 JS 的交互。
JavaScriptCore中類及協(xié)議:
JSContext
給JavaScript提供運(yùn)行的上下文環(huán)境懂更,像是前端開發(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)的類型脖阵,一個(gè)用來(lái)處理iOS中所有可能存在的JavaScript執(zhí)行后產(chǎn)生Value的類皂股,可以用來(lái)轉(zhuǎn)換基本數(shù)據(jù)類型
JSManagedValue
管理數(shù)據(jù)和方法的類,包含一個(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ù)寫進(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 類型到 OC 類型的處理并不一致角骤,具體如下:
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 類优烧,此類必須遵守一個(gè)‘遵守 JSExport 協(xié)議’的協(xié)議蝉揍,如果想要將一個(gè)自定義類的方法暴露給外部的JavaScript使用,那么這個(gè)類必須遵守JSExport協(xié)議匙隔,JSExport協(xié)議提供了一種聲明式的方法去向JavaScript代碼導(dǎo)出Objective-C的實(shí)例類及其實(shí)例方法疑苫,類方法和屬性:
@protocol PersonJSExport <JSExport>
//將 JS 中的調(diào)用方法名與model類中的相對(duì)應(yīng)
JSExportAs(a, - (void)nslog:(NSString *)string);
@end
在model類中實(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類賦值給 JS 中定義的一個(gè)對(duì)象挺勿,在點(diǎn)擊 html 頁(yè)面上的 button 后,JS 執(zhí)行 OC 方法喂柒。