iOS開發-JavaScriptCore的使用-WebiOS開發

        就目前市面上的App來說很多都是原生+ H5的作爲基底開發的,因爲H5在頁面交互的處理以及動畫效果存在很大的優勢,所以現在開發的App原生界面裏面摻雜着H5頁面是很常見的。

        最近在開發的App也是,使用谷歌地圖,在iOS端原生的需要付費才能提供更多的支持,而在Web端api都是開放使用的,而且就於開發而言Web端更爲簡單。

        大量的趨勢造就了需求,在技術羣很多人都在問原生和H5怎麼用來交互,我就趁着最近的工作給大家寫一篇相關的開發文檔,簡單解釋一下JavaScriptCore的使用。


1.什麼是JavaScriptCore?
        JavaScriptCore是蘋果Safari瀏覽器的JavaScript引擎,是基於webkit中以C/C++實現的JavaScriptCore的一個包裝,在舊版本iOS開發中,很多開發者也會自行將webkit的庫引入項目編譯使用。不過在iOS7之後蘋果就把它做成了一個標準庫,提供開發者使用

2.JavaScriptCore中的類
        在項目中引入JavaScriptCore後,鏈到頭文件中,除了大段的Copyright註釋可以看到裏面只要引入了5個文件,每個文件裏都定義跟文件名對應的類:
· JSContext
· JSValue
· JSManagedValue
· JSVirtualMachine
· JSExport


3.JSContext和JSValue
        JSVirtualMachine爲JavaScript的運行提供了底層資源,JSContext就爲其提供着運行環境,通過- (JSValue *)evaluateScript:(NSString *)script;方法就可以執行一段JavaScript腳本,並且如果其中有方法、變量等信息都會被存儲在其中以便在需要的時候使用。而JSContext的創建都是基於JSVirtualMachine:- (id)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine;,如果是使用- (id)init;進行初始化,那麼在其內部會自動創建一個新的JSVirtualMachine對象然後調用前邊的初始化方法。

        JSValue則可以說是JavaScript和Object-C之間互換的橋樑,它提供了多種方法可以方便地把JavaScript數據類型轉換成Objective-C,或者是轉換過去。

4.Objective-C調用JavaScript
        oc想要調用js代碼的話,先創建一個JSContext對象實例,接着通過evaluateScript加載js代碼到context對象中,然後獲取js對象,如果爲js函數對象,通過callWithArguments調用該js函數,並且可以以數組的方式傳遞參數。

//js代碼段
var appendString = function(name) {
return 'string:' + name;
};
var arr = [1, 2 , 'hello world'];

//m文件
NSString *jsPath = [[NSBundle mainBundle] pathForResource:@"test"ofType:@"js"];
NSString *jsContent = [NSString stringWithContentsOfFile:jsPath encoding:NSUTF8StringEncoding error:nil];

JSContext *context = [[JSContext alloc] init];
[context evaluateScript:jsContent];

JSValue *value = [context[@"appendString"] callWithArguments:@[@"hello"]];
JSValue *value1 = context[@"arr"];

NSLog(@"appendString:%@",[value toString] );//appendString:string:hello
NSLog(@"arr:%@",[value1 toArray] );

5.JavaScript調用Objective-C

JS調用OC有兩種實現方式:
(1)Block方式
        我們可以通過block的方式將oc代碼暴露給js,JavaScriptCore會自動將oc block包裝在js函數中,我們就可以直接在js中調用該block函數

    JSContext *context = [[JSContext alloc] init];
    context[@"sayhi"] = ^(NSString *name) {
        NSLog(@"say hi to %@",name);
    };
    [context evaluateScript:@"sayhi('Greg')"]; 

(2)JSExport協議
如果你到頭文件中去查看JSExport協議,你會發現這個協議其實沒有定義任何東西。JavaScriptCore提供這個協議用來將oc的方法跟屬性暴露給js調用,其中@property會轉換成js的getter和setter方法,實例方法會轉換成js函數,而類方法則轉換成js中global object的方法。

//定義需要暴露給js的內容,這裏我們只暴露personName和queryPersonName接口
@protocol PersonProtocol <JSExport>
@property(nonatomic,copy)NSString *personName;
-(NSString *)queryPersonName;
@end

//Person實現PersonProtocol協議,而自己定義的age和queryPersonAge接口不暴露給js
@interface Person : NSObject <PersonProtocol>
@property(nonatomic,assign)NSInteger age;
-(NSInteger)queryPersonAge;
@end

@implementation Person
@synthesize personName = _personName;

-(NSString *)queryPersonName{
    return self.personName;
}
-(NSInteger)queryPersonAge{
    return self.age;
}
@end

JSContext *context = [[JSContext alloc] init];

//創建Person類的對象,將他賦值給js對象
Person *person=[Person new];
person.personName = @"Greg";
person.age = 27;
context[@"person"]=person;

//可以調用獲取PersonProtocol暴露的內容
NSString *personName = [[context evaluateScript:@"person.personName"] toString];
NSString *personName1 = [[context evaluateScript:@"person.queryPersonName()"] toString];

//js無法調用跟age相關的內容
NSInteger age = [[context evaluateScript:@"person.age"] toInt32];
NSInteger age1 = [[context evaluateScript:@"person.queryPersonAge()"] toInt32];

6.JavaScriptCore在Web容器中的使用

在UIWebView中,我們可以在- (void)webViewDidFinishLoad:(UIWebView *)webView方法中,通過KVC的方式獲取到當前容器的JSContent對象,通過該對象,我們就可以方便的進行hybrid操作。

JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

功能例子:在html中調研OC代碼中的分享功能和調用相機功能。

(1)HelloWord.html代碼如下:
function jsCallNative(){
    WBBridge.callCamera();
}
function jsCallNative2(){
    var shareInfo = "分享內容";
    var str = WBBridge.share(shareInfo);
    alert(str);
}<input type="button" οnclick="jscallnative()" value="jscallnative" ><input type="button" οnclick="jscallnative2()" value="jscallnative2" >
</input type="button" οnclick="jscallnative2()" value="jscallnative2" ></input type="button" οnclick="jscallnative()" value="jscallnative" >

(2)實現一個遵守JSExport的協議WebViewJSExport
@protocol WebViewJSExport - (void)callCamera;
- (NSString*)share:(NSString*)shareString;
@end

(3)當前VC需要實現WebViewJSExport
@interface ViewController ()<uiwebviewdelegate,webviewjsexport>@property (nonatomic, strong) JSContext *context;
@property (nonatomic, strong) UIWebView *webView;
@end
@implementation ViewController
-(void)initWebView{
    self.context = [[JSContext alloc] init];

    _webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
    _webView.delegate = self;
    [self.view addSubview:_webView];

    NSURL *url = [[NSURL alloc] initWithString:@"http://localhost:8080/myDiary/HelloWorld.html"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [self.webView loadRequest:request];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView{

    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    _context = context;

    // 將本對象與 JS 中的 WBBridge 對象橋接在一起,在 JS 中 WBBridge 代表本對象
    [_context setObject:self forKeyedSubscript:@"WBBridge"];
    _context.exceptionHandler = ^(JSContext* context, JSValue* exceptionValue) {
        context.exception = exceptionValue;
        NSLog(@"異常信息:%@", exceptionValue);
    };
}
- (void)callCamera{
    NSLog(@"調用相機");
}
- (NSString*)share:(NSString*)shareString{
    NSLog(@"分享::::%@",shareString);
    return @"分享成功";
}
@end</uiwebviewdelegate,webviewjsexport>





發佈了79 篇原創文章 · 獲贊 79 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章