JavaScriptCore框架詳解

目前愈來愈多的移動應用採用Hybird App模式來開發,即混合了 Native技術 與 Web技術 進行開發。在iOS開發中,JavaScriptCore框架支持JS與OC直接互相調用,從而實現動態化方案。html

JavaScriptCore框架簡介

JavaScriptCore框架是蘋果在iOS7引入的一個框架,該框架讓 Objective-C 和 JavaScript 代碼 互通,即支持在Objective-C中執行js代碼,也支持js代碼中執行OC代碼。瀏覽器

瀏覽器中能執行JS腳本是由於瀏覽器中內置了JavaScript引擎。JavaScriptCore是蘋果Safari瀏覽器的JavaScript引擎,而JavaScriptCore框架是基於Objective-C實現了對JavaScriptCore的封裝,提供了Objective-C接口,讓開發者可以在在iOS App中處理JavaScript腳本。框架

簡單使用

先來簡單瞭解下在iOS Native開發中如何使用JavaScriptCore框架函數

#import "JavaScriptCore/JavaScriptCore.h" // 引入頭文件
    ...
    ...
    // 建立一個JSContext對象
    JSContext *jsContext = [[JSContext alloc] init];
    
    // 執行JS代碼 計算js變量a和b之和
    [jsContext evaluateScript:@"var a = 1;var b = 2;"];
    JSValue *result = [jsContext evaluateScript:@"a + b"];
    NSInteger sum = [result toInt32];
    NSLog(@"%ld", (long)sum);    // 3
複製代碼

上面代碼中作了以下幾件事情ui

  1. 引入"JavaScriptCore/JavaScriptCore.h"頭文件
  2. 建立了一個JSContext類對象jsContext
  3. jsContext調用evaluateScript:方法執行了兩條js語句,並獲得執行結果result,是一個JSValue類對象
    var a = 1;var b = 2;
    a + b
    複製代碼
  4. result調用toInt32方法,返回數值類型的sum並打印,輸出結果爲3。

這裏涉及了JavaScriptCore框架中的兩個核心類:JSContext類 和 JSValue類。這裏先說明下他們的做用,後續再詳細介紹。
JSContext類: 一個JSContext表示了一次JS的執行環境。在iOS開發中,能夠經過建立一個JSContext去調用JS腳本,訪問一些JS定義的值和函數,同時也提供了讓JS訪問Native對象、方法的接口。
JSValue類: JS側的代碼執行結果均可以從JSContext中獲取而後賦值給JSValue對象,JSValue是保證JS端和Native的方法能互相調用的橋樑。this

更多使用

OC代碼中執行JS語句

- (void)ocEvaluateScript {
    // 建立一個JSContext對象
    JSContext *jsContext = [[JSContext alloc] init];
    
    // 執行JS代碼 計算js變量a和b之和
    [jsContext evaluateScript:@"var a = 1;var b = 2;"];
    NSInteger sum = [[jsContext evaluateScript:@"a + b"] toInt32];
    NSLog(@"%ld", (long)sum);    // 3
    
    // 經過下標獲取變量、方法
    [jsContext evaluateScript:@"var names = ['Same','Jack','Bob']"];
    JSValue *names = jsContext[@"names"];
    JSValue *initialName = names[0];
    NSLog(@"%@", initialName.toString); // 'Same'
    
    // 定義方法並調用
    [jsContext evaluateScript:@"var addFunc = function(a, b) { return a + b }"];
    JSValue *result = [jsContext evaluateScript:@"addFunc(a, b)"];
    NSLog(@"%@", result.toNumber);  // 3
    
    // 也能夠OC傳參
    JSValue *addFunc = jsContext[@"addFunc"];
    JSValue *addResult = [addFunc callWithArguments:@[@10, @30]];
    NSLog(@"%d", addResult.toInt32);    // 40
}
複製代碼

OC代碼中執行JS腳本內容

ocEvaluateScript.js腳本lua

function subtractFunc(a, b) {
    return a - b;
}
console.log(subtractFunc(15, 7))
console.log("Hello ocEvaluateScript")
複製代碼

OC代碼中加載ocEvaluateScript.js腳本並執行spa

- (void)ocEvaluateScriptFile {
    JSContext *jsContext = [[JSContext alloc] init];
    NSString *path = [[NSBundle mainBundle] pathForResource:@"ocEvaluateScript" ofType: @"js"];
    NSString *layoutJS = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
    [jsContext evaluateScript:layoutJS];
    
    JSValue *subtractFunc = jsContext[@"subtractFunc"];
    JSValue *subtractResult = [subtractFunc callWithArguments:@[@20, @10]];
    NSLog(@"%d", subtractResult.toInt32);    // 10
}
複製代碼
  1. OC代碼中加載並執行了ocEvaluateScript.js,而後調用了subtractFunc方法,控制檯輸出結果爲10;
  2. 打開safari瀏覽器,運行程序後,會彈出網頁檢查器,其控制檯輸出ocEvaluateScript.js腳本的執行結果

JS腳本執行OC代碼

scriptEvaluateOC.js腳本內容,addFunc方法和subtractFun方法是在OC代碼中定義的。3d

console.log(addFunc(5, 5))

console.log(subtractFunc(8, 5))

console.log("Hello scriptEvaluateOC")
複製代碼

OC代碼中定義addFunc方法和subtractFun方法,並加載執行scriptEvaluateOC.jscode

- (void)scriptEvaluateOC {
    JSContext *jsContext = [[JSContext alloc] init];
    jsContext[@"addFunc"] = ^(NSInteger a, NSInteger b) {
        return a + b;
    };
    JSValue *addResult = [jsContext evaluateScript:@"addFunc(3, 4)"];
    NSLog(@"%@", addResult.toNumber);  // 7
    
    // setObject:forKeyedSubscript:方法用來向JSContext環境的全局對象中添加屬性
    [jsContext setObject:^(NSInteger a, NSInteger b) {
        NSArray *args = [JSContext currentArguments];
        NSLog(@"argu are %@", args);
        return a - b;
    } forKeyedSubscript:@"subtractFunc"];
    JSValue *subtractResult = [jsContext evaluateScript:@"subtractFunc(4, 3)"];
    NSLog(@"%@", subtractResult.toNumber);  // 1
 
    NSString *path = [[NSBundle mainBundle] pathForResource:@"scriptEvaluateOC" ofType: @"js"];
    NSString *layoutJS = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
    [jsContext evaluateScript:layoutJS];
}
複製代碼

打開safari瀏覽器,運行程序後,會彈出網頁檢查器,其控制檯輸出scriptEvaluateOC.js腳本的執行結果

JavaScriptCore框架詳解

JavaScriptCore框架包含的頭文件真的是很是的少,以下所示,

整個框架的結構圖參考JavaScriptCore框架詳解

下面對各個文件逐一分析

JavaScript和JavaScriptCore

JSContent

JSValue

JSManagedValue

JSExport

JSVirtualMachine

Native UI動態化方案

Native UI動態化是指在js腳本中編寫Native的UI信息,而後由Native加載執行,解析轉換成Native的UI實現。這種方案能夠支持js層作了關於渲染信息的一些修改後,Native只要從新加載執行js就能夠更新UI信息而不須要從新編譯,打包發版。 這裏簡單的實現一個UILabel標籤的示例來講明下思路 view.js

(function(){
  return render();
  })();

//JS標籤類
function Label(rect,text,color){
    this.rect = rect;
    this.text = text;
    this.color = color;
    this.typeName = "Label";
}

//JS Rect類
function Rect(x,y,width,height){
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
}

//渲染方法 界面的渲染寫在這裏面
function render(){
    var rect = new Rect(20,100,280,30);
    var label = new Label(rect,"Hello World", "0xff0000");
    return label
}
複製代碼

Native邏輯 在ViewController.m中定義render方法並執行,主要邏輯以下:

#define HEXCOLOR(hexValue)              [UIColor colorWithRed : ((CGFloat)((hexValue & 0xFF0000) >> 16)) / 255.0 green : ((CGFloat)((hexValue & 0xFF00) >> 8)) / 255.0 blue : ((CGFloat)(hexValue & 0xFF)) / 255.0 alpha : 1.0]

#define HEXACOLOR(hexValue, alphaValue) [UIColor colorWithRed : ((CGFloat)((hexValue & 0xFF0000) >> 16)) / 255.0 green : ((CGFloat)((hexValue & 0xFF00) >> 8)) / 255.0 blue : ((CGFloat)(hexValue & 0xFF)) / 255.0 alpha : (alphaValue)]


-(void)render {
    NSString *path = [[NSBundle mainBundle] pathForResource:@"view" ofType: @"js"];
    NSString *layoutJS = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
   
    JSContext *jsContext = [[JSContext alloc] init];
    JSValue * subValue = [jsContext evaluateScript:layoutJS];
    
    UILabel * label = [UILabel new];
    label.frame = CGRectMake(subValue[@"rect"][@"x"].toDouble, subValue[@"rect"][@"y"].toDouble, subValue[@"rect"][@"width"].toDouble, subValue[@"rect"][@"height"].toDouble);
    label.text = subValue[@"text"].toString;
    label.textColor = HEXCOLOR(subValue[@"color"].toInt32);
    [self.view addSubview:label];
}
複製代碼

界面展現結果

參考資料

iangeli.com/2017/03/20/…

相關文章
相關標籤/搜索