JSContext --- 在OC中創建JavaScript運行的上下文環境
- (instancetype)init; // 創建JSContext對象,獲得JavaScript運行的上下文環境 // 在特定的對象空間上創建JSContext對象,獲得JavaScript運行的上下文環境 - (instancetype)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine; // 運行一段js代碼,輸出結果為JSValue類型 - (JSValue *)evaluateScript:(NSString *)script; // iOS 8.0以后可以調用此方法 - (JSValue *)evaluateScript:(NSString *)script withSourceURL:(NSURL *)sourceURL NS_AVAILABLE(10_10, 8_0); // 獲取當前正在運行的JavaScript上下文環境 + (JSContext *)currentContext; // 返回結果當前執行的js函數 function () { [native code] } ,iOS 8.0以后可以調用此方法 + (JSValue *)currentCallee NS_AVAILABLE(10_10, 8_0); // 返回結果當前方法的調用者[object Window] + (JSValue *)currentThis; // 返回結果為當前被調用方法的參數 + (NSArray *)currentArguments; // js的全局變量 [object Window] @PRoperty (readonly, strong) JSValue *globalObject;JSValue --- JavaScript中的變量和方法,可以轉成OC數據類型,每個JSValue都和JSContext相關聯并且強引用context
@textblock Objective-C type | JavaScript type --------------------+--------------------- nil | undefined NSNull | null NSString | string NSNumber | number, boolean NSDictionary | Object object NSArray | Array object NSDate | Date object NSBlock (1) | Function object (1) id (2) | Wrapper object (2) Class (3) | Constructor object (3) @/textblock // 在context創建BOOL的JS變量 + (JSValue *)valueWithBool:(BOOL)value inContext:(JSContext *)context; // 將JS變量轉換成OC中的BOOL類型 - (BOOL)toBool; // 修改JS對象的屬性的值 - (void)setValue:(id)value forProperty:(NSString *)property; // JS中是否有這個對象 @property (readonly) BOOL isUndefined; // 比較兩個JS對象是否相等 - (BOOL)isEqualToObject:(id)value; // 調用者JSValue對象為JS中的方法名稱,arguments為參數,調用JS中Window直接調用的方法 - (JSValue *)callWithArguments:(NSArray *)arguments; // 調用者JSValue對象為JS中的全局對象名稱,method為全局對象的方法名稱,arguments為參數 - (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments; // JS中的結構體類型轉換為OC + (JSValue *)valueWithPoint:(CGPoint)point inContext:(JSContext *)context;JSExport --- JS調用OC中的方法和屬性寫在繼承自JSExport的協議當中,OC對象實現自定義的協議 // textFunction -- JS方法 // - (void) ocTestFunction:(NSNumber *)value sec:(NSNumber *)number -- OC方法 JSExportAs (textFunction,- (void) ocTestFunction:(NSNumber *)value sec:(NSNumber *)number);JSManagedValue --- JS和OC對象的內存管理輔助對象,主要用來保存JSValue對象,解決OC對象中存儲js的值,導致的循環引用問題
JSManagedValue *_jsManagedValue = [JSManagedValue managedValueWithValue:jsValue];[_context.virtualMachine addManagedReference:_jsManagedValue];JSManagedValue本身只弱引用js值,需要調用JSVirtualMachine的addManagedReference:withOwner:把它添加到JSVirtualMachine中,這樣如果JavaScript能夠找到該JSValue的Objective-C owner,該JSValue的引用就不會被釋放。
JSVirtualMachine --- JS運行的虛擬機,有獨立的堆空間和垃圾回收機制,運行在不同虛擬機環境的JSContext可以通過此類通信。
JS中,點擊事件直接調用方法方式JS和Native代碼的互調如下:
1.1 JS代碼如下
<html> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta content="width=device-width,initial-scale=1,user-scalable=no" name="viewport"><body> <script type="text/javascript"> var nativeCallJS = function(parameter) { alert (parameter); }; </script> <button type="button" onclick = "jsCallNative('jsparameter')" style="width:100%; height:30px;"/>調用OC代碼</button></body></html>1.2 OC代碼如下
- (void)__jsLogic { self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception){ NSLog(@"JS代碼執行中的異常信息%@", exception); }; self.jsContext = [self valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; self.jsContext[@"jsCallNative"] = ^(NSString *paramer){ JSValue *currentThis = [JSContext currentThis]; JSValue *currentCallee = [JSContext currentCallee]; NSArray *currentParamers = [JSContext currentArguments]; dispatch_async(dispatch_get_main_queue(), ^{ /** * js調起OC代碼,代碼在子線程,更新OC中的UI,需要回到主線程 */ }); NSLog(@"JS paramer is %@",paramer); NSLog(@"currentThis is %@",[currentThis toString]); NSLog(@"currentCallee is %@",[currentCallee toString]); NSLog(@"currentParamers is %@",currentParamers); }; JSValue *jsMethod = self.jsContext[@"nativeCallJS"]; [jsMethod callWithArguments:@[@"nativeCallJS"]]; }1.3 OC運行結果,彈出HTML中的alert提示 2016-08-05 17:57:08.974 LeWebViewPro[38150:3082770] JS paramer is jsParameter 2016-08-05 17:57:08.975 LeWebViewPro[38150:3082770] currentThis is [object Window] 2016-08-05 17:57:08.975 LeWebViewPro[38150:3082770] currentCallee is function () { [native code]} 2016-08-05 17:57:08.975 LeWebViewPro[38150:3082770] currentParamers is ( jsParameter )JS中,點擊事件等事件通過調用全局對象的方法調用方法,JS和Native代碼的互調如下:
2.1 JS代碼如下
<html> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta content="width=device-width,initial-scale=1,user-scalable=no" name="viewport"> <body> <script type="text/javascript"> globalObject = new Object(); globalObject.name = 100; globalObject.nativeCallJS = function (parameter) { alert (parameter); }; </script> <button type="button" onclick = "globalObject.jsCallNative('jsParameter')" style="width:100%; height:30px;"/>調用OC代碼</button> </body> </html>2.2 OC代碼如下2.2.1 JSManager 代碼,負責執行JS中的方法
#import <JavaScriptCore/JavaScriptCore.h>#import <Foundation/Foundation.h>@protocol LeJSExport <JSExport>JSExportAs (jsCallNative,- (void) jsCallNative:(NSString *)jsParameter);@end@interface JSManager : NSObject<LeJSExport>@end-----.M文件-----#import "JSManager.h"@implementation JSManager- (void)jsCallNative:(NSString *)jsParameter{ JSValue *currentThis = [JSContext currentThis]; JSValue *currentCallee = [JSContext currentCallee]; NSArray *currentParamers = [JSContext currentArguments]; dispatch_async(dispatch_get_main_queue(), ^{ /** * js調起OC代碼,代碼在子線程,更新OC中的UI,需要回到主線程 */ }); NSLog(@"JS paramer is %@",jsParameter); NSLog(@"currentThis is %@",[currentThis toString]); NSLog(@"currentCallee is %@",[currentCallee toString]); NSLog(@"currentParamers is %@",currentParamers);}@end2.2.2 包含UIWebView類的代碼,負責調起JS中的方法
- (void)nativeCallJS{ self.jsManager = [[JSManager alloc] init]; self.jsContext = [self valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception){ NSLog(@"JS代碼執行中的異常信息%@", exception); }; self.jsContext[@"globalObject"] = self.jsManager; //1.OC方法調起JS JSValue *varibleStyle = self.jsContext[@"globalObject"]; [varibleStyle invokeMethod:@"nativeCallJS" withArguments:@[@100]]; //2.OC腳本調起JS NSString *jsScript = [NSString stringWithFormat:@"globalObject.nativeCallJS('%@')",@100]; [self.jsContext evaluateScript:jsScript];}2.3 OC運行結果,彈出HTML中的alert提示
2016-08-07 10:30:11.444 LeWebViewPro[46674:3253852] JS paramer is jsParameter 2016-08-07 10:30:11.444 LeWebViewPro[46674:3253852] currentThis is [object JSManager] 2016-08-07 10:30:11.444 LeWebViewPro[46674:3253852] currentCallee is function () { [native code] } 2016-08-07 10:30:11.445 LeWebViewPro[46674:3253852] currentParamers is ( jsParameter )線程問題
JavaScriptCore中提供的API都是線程安全的,一個JSVirtualMachine在一個線程中,它可以包含多個JSContext,而且相互之間可以傳值,為了確保線程安全,這些context在運行的時候會采用鎖,可以認為是串行執行。JS調用OC的回調方法,是在子線程,所以需要更新OC中的UI的話,需要切換到主線程內存問題
oc中使用ARC方式管理內存(基于引用計數),但JavaScriptCore中使用的是垃圾回收方式,其中所有的引用都是強引用,但是我們不必擔心其循環引用,js的垃圾回收能夠打破這些強引用,有些情況需要考慮如下js調起OC回調的block中獲取JSConetxt容易循環引用
self.jsContext[@"jsCallNative"] = ^(NSString *paramer){ // 會引起循環引用 JSValue *value1 = [JSValue valueWithNewObjectInContext: self.jsContext]; // 不會引起循環引用 JSValue *value = [JSValue valueWithNewObjectInContext: [JSContext currentContext]];};JavaScriptCore中所有的引用都是強引用,所以在OC中需要存儲JS中的值的時候,需要注意
在oc中為了打破循環引用我們采用weak的方式,不過在JavaScriptCore中我們采用內存管理輔助對象JSManagedValue的方式,它能幫助引用技術和垃圾回收這兩種內存管理機制之間進行正確的轉換
OC讀取JS文件,并相互通信
NSString *jsPath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"js"]; NSString *jsContent = [[NSString alloc] initWithContentsOfFile:jsPath encoding:NSUTF8StringEncoding error:nil]; JSContext *jsContext = [[JSContext alloc] init]; //捕獲運行js腳本的錯誤信息 jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) { context.exception = exceptionValue; NSLog(@"異常信息:%@", exceptionValue); }; //js腳本添加到當前的js執行環境中 [jsContext evaluateScript:jsContent]; self.jsManager = [[JSManager alloc] init]; jsContext[@"globalObject"] = self.jsManager; ... ...jS與OC的交互與通過UIWebView相同
JavaScript和Objective-C交互的那些事
說說JavaScriptCore
新聞熱點
疑難解答