經過前兩天的 JavaScript 的粗略學習,來看看 JavaScriptCore 。
JavaScriptCore 是封裝了JavaScript和Objective-C橋接的Objective-C API,只要用很少的代碼,就可以做到JavaScript調用Objective-C,或者Objective-C調用JavaScript。
- OC 調用 JS
// JScript
NSString *path = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"js"];
NSString *javaScript = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
// JSContext 初始化
JSContext *context = [[JSContext alloc] init];
// 獲取
[context evaluateScript:javaScript];
// 調用
JSValue *function = [context objectForKeyedSubscript:@"testAction"];
// test result
JSValue *result = [function callWithArguments:@[@5]];
NSLog(@"result == %@",result); // ==> 15
test.js
var testAction = function(n) {
if (n < 1) return;
if (n === 1) return 1;
return n + testAction(n - 1);
};
- JS 調用 OC
UIWebView UIWebViewDelegate
- (void)webViewDidFinishLoad:(UIWebView *)webView {
// 通過UIWebView獲得網頁中的JavaScript執行環境
JSContext *context = [webView valueForKeyPath:
@"documentView.webView.mainFrame.javaScriptContext"];
// 設置處理異常的block回調
[context setExceptionHandler:^(JSContext *context, JSValue *exception) {
NSLog(@"error: %@", exception);
}];
// 以 block 形式關聯 JavaScript function
__weak typeof(self) weakSelf = self;
context[@"alert"] = ^(NSString *str) {
__strong typeof(weakSelf) strongSelf = weakSelf;
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Test" message:nil preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ensureAction = [UIAlertAction actionWithTitle:@"Ensure" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"Test Ensure");
}];
[alertController addAction:ensureAction];
dispatch_async(dispatch_get_main_queue(), ^{
[strongSelf presentViewController:alertController animated:YES completion:nil];
});
};
}
而 WKWebView 不支持JavaScriptCore的方式但提供message handler的方式為JavaScript 與Objective-C 通信
- (void)userContentController:(WKUserContentController *)userContentController
didReceiveScriptMessage:(WKScriptMessage *)message;
當然得增加此方法的
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.userContentController = [[WKUserContentController alloc] init];
WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];
[config.userContentController addScriptMessageHandler:self name:@"nativeCustomName"];
// 在用完后記得移除
// [config.userContentController removeScriptMessageHandlerForName:@"nativeCustomName"];
此處再來從 網上對 JavaScriptCore 了解 下 JavaScriptCore 的基本知識點:
- JSValue: 代表一個JavaScript實體,一個JSValue可以表示很多JavaScript 原始類型例如 boolean, integers, doubles,甚至包括對象和函數。
- JSManagedValue: 本質上是一個JSValue,但是可以處理內存管理中的一些特殊情形,它能幫助引用技術和垃圾回收這兩種內存管理機制之間進行正確的轉換。
- JSContext: 代表JavaScript的運行環境,你需要用JSContext來執行JavaScript代碼。所有的JSValue都是捆綁在一個JSContext上的。
- JSExport: 這是一個協議,可以用這個協議來將原生對象導出給JavaScript,這樣原生對象的屬性或方法就成為了JavaScript的屬性或方法,非常神奇。
- JSVirtualMachine: 代表一個對象空間,擁有自己的堆結構和垃圾回收機制。大部分情況下不需要和它直接交互,除非要處理一些特殊的多線程或者內存管理問題。
JSValue
JSValue是我們需要處理的主要數據類型:它可以表示任何可能的Javascript值。一個JSValue被綁定到其存活的JSContext對象中。任何來源于上下文對象的值都是JSValue類型。
JSContext
JavaScript 執行的環境,同時也通過 JSVirtualMachine 管理著所有對象的生命周期,每個JSValue都和JSContext相關聯并且強引用context。
- (JSValue *)evaluateScript:(NSString *)script;
- (JSValue *)objectForKeyedSubscript:(id)key;
//以 block 形式關聯 JavaScript function
self.context[@"log"] = ^(NSString *str) {
NSLog(@"%@", str);
};
下面這個例子,很好的解釋了上面兩個方法,以及對 JSValue 的一個大致了解
NSString *jsString = @"function test(a,b) {return a+b}";
[self.context evaluateScript:jsString];
JSValue *testValue = [self.context[@"test"] callWithArguments:@[@5, @1]];
NSLog(@"testValue===%@", [testValue toString]); // 6
JSExport
例如 JS 調用 OC 中的方法的時候,很方便也很熟悉,代理的感覺。
#define JSExportAs(PropertyName, Selector) \
@optional Selector __JS_EXPORT_AS__##PropertyName:(id)argument; @required Selector
和我們平常使用 Delegate 一樣,先定義 protocol ,然后一一對應就好啦
@protocol TestJSExport <JSExport>
/**
* testAction JS 中的方法名
* void OC 中的實現方法
*/
JSExportAs (testAction, - (void)testAction);
@end
self.context[@"app"] = self; // 以 JSExport 協議關聯 native 的方法
- (void)testAction {
NSLog(@" JS Call Oc Action");
}
// JS 中的Button 的實現
<input type="button" value="testAction" onclick="app.testAction();" />
暫時記錄到此,接下來就是真正的實戰啦。
備注參考:
http://nshipster.cn/javascriptcore/
https://hjgitbook.gitbooks.io/ios/content/04-technical-research/04-javascriptcore-note.html