iOS7之后,蘋果推出了JavaScriptCore,是WebKit的一個重要組成部分,對JS進(jìn)行解析和提供運(yùn)行環(huán)境。
JSContext
JS上下文,提供運(yùn)行環(huán)境,同時也通過JSVirtualMachine管理著所有對象的生命周期,每個JSValue都和JSContext相關(guān)聯(lián)并且強(qiáng)引用context。
JSValue
JS和OC互換的橋梁,它提供了多種方法實(shí)現(xiàn)OC與JS之間的互換。
JSManagedValue
JS和OC對象的內(nèi)存管理輔助對象。由于JS內(nèi)存管理是垃圾回收,并且JS中的對象都是強(qiáng)引用,而OC是引用計數(shù)。如果雙方相互引用,勢必會造成循環(huán)引用,而導(dǎo)致內(nèi)存泄露。我們可以用JSManagedValue保存JSValue來避免。
JSVirtualMachine
JS運(yùn)行的虛擬機(jī),有獨(dú)立的堆空間和垃圾回收機(jī)制。- (id)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine;,如果是使用- (id)init;進(jìn)行初始化,那么在其內(nèi)部會自動創(chuàng)建一個新的JSVirtualMachine對象然后調(diào)用前邊的初始化方法。
JSExport
一個協(xié)議,如果JS對象想直接調(diào)用OC對象里面的方法和屬性,那么這個OC對象只要實(shí)現(xiàn)這個JSExport協(xié)議就可以了。
OC中調(diào)用JS
從JS環(huán)境里取函數(shù)或者變量,就是將一段JS代碼通過JSContext轉(zhuǎn)化為OC執(zhí)行。
#pragma mark - 加載一段JS代碼
-(void)OCCallJS
{
JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
NSString *jsString = [NSString stringWithFormat:@"alert('%@')",@"這是OC調(diào)用JS"];
[context evaluateScript:jsString];
}
當(dāng)JS代碼多,直接食用字符串轉(zhuǎn)化可能會出現(xiàn)錯誤,此時可以讀取本地.html/.js文件來實(shí)現(xiàn)
#pragma mark - 加載本地.js文件
-(void)loadScriptWithContext:(JSContext *)context AndFileName:(NSString *)fileName
{
NSString *filePath = [NSString stringWithFormat:@"%@/JS/%@",[[NSBundle mainBundle] resourcePath],fileName];
NSString *script = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
[context evaluateScript:script];
}
//加載.html文件
NSURL *htmlURL = [[NSBundle mainBundle]URLForResource:@"js.html" withExtension:nil];
NSURLRequest *request = [NSURLRequest requestWithURL:htmlURL];
[self.webView loadRequest:request];
JS中調(diào)用OC
方法一:將block注入JSContext上下文,在JS執(zhí)行的時候調(diào)用OC原生方法
方法二:將OC對象注入JSContext上下文,在JS執(zhí)行的時候調(diào)用OC原生方法
一般寫在UIWebView的delegate中
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
//JS調(diào)OC方法一
//首先創(chuàng)建JSContext 對象(此處通過當(dāng)前webView的鍵獲取到j(luò)scontext)
JSContext *context1 = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
context1[@"JSCallOCWithBlock"] = ^(NSString *str){
NSLog(@"OC的Block被調(diào)用:%@",str);
};
//JS調(diào)OC方法二
JSContext *context2 = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
GemJSObject *jsObject = [[GemJSObject alloc]init]; //創(chuàng)造一個對象
jsObject.x = 10;
context2[@"JSCallOCWithObject"] = jsObject; //將對象注入JS運(yùn)行環(huán)境
}
OC對象中的代碼
#pragma mark - GemJSObject.h
#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>
@protocol GemJSObjectExport <JSExport>
//定義要暴露給JS的屬性和方法,JS就可以對其操作了
@property int x;
-(void)nslog:(NSString *)str;
@end
@interface GemJSObject : NSObject<GemJSObjectExport>
-(void)nslog:(NSString *)str;
@end
#pragma mark - GemJSObject.m
#import "GemJSObject.h"
@implementation GemJSObject
@synthesize x;
-(void)nslog:(NSString *)str
{
NSLog(@"%d",self.x * self.x);
NSLog(@"OC的nslog方法被調(diào)用:%@",str);
}
@end
js.html文件中的JS代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>test javascript</title>
</head>
<body>
<div>
<!--
調(diào)用方法(并傳入?yún)?shù),若block中無參,則此參數(shù)無用)
-->
<button onclick="JSCallOCWithBlock('JS調(diào)用OC方法,并傳參(通過block)');">JS_OC_Block</button>
<br>
<!--
調(diào)用對象的nslog方法(并傳入?yún)?shù)),對象需要傳入自定義的NSObject對象(遵循JSExport協(xié)議)
-->
<button onclick="JSCallOCWithObject.nslog('JS調(diào)用OC方法,并傳參(通過GemJSObject對象)');">JS_OC_Object</button>
</div>
</body>
</html>
方法三:攔截非正常的URL
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
//JS調(diào)OC方法三:攔截不正常URL
if ([request.URL.absoluteString isEqualToString:@"非正常的URL頭"]) {
//調(diào)用OC的方法
[self handleCustomAction:request.URL];
return NO;
}
return YES;
}
-(void)handleCustomAction:(NSURL *)url
{
if ([url.host isEqualToString:@"getLocation"]) {
[self getLocation];
}
}
-(void)getLocation
{
//獲取位置信息,將結(jié)果返回給js
NSString *jsStr = [NSString stringWithFormat:@"setLocation('%@')",@"北京市朝陽區(qū)XXX號"];
[self.webView stringByEvaluatingJavaScriptFromString:jsStr];
/**
補(bǔ)充:在JS代碼中調(diào)OC,也需要傳參數(shù)到OC中,就像一個get請求一樣,把參數(shù)放在后面。
function shareClick() {
loadURL("haleyAction://shareClick?title=測試分享的標(biāo)題&content=測試分享的內(nèi)容&url=http://www.baidu.com");
}
*/
}
實(shí)際應(yīng)用中,可能會遇見瀏覽webView圖片的情況,我的GitHub有相關(guān)demo可參考瀏覽webView圖片demo