JavaScriptCore.framework的ios與js交互(二)

認識JavaScriptCore.framework

githubDemo:https://github.com/wangjinshan/JSCDemo

項目演示

11.gif

94E8E789-02BE-4C1B-80A0-B5272F19BA47.png
107BFC2C-26E3-4395-BE81-8EFD558D3F52.png

正文

JavaScriptCore.framework 是蘋果在ios7之后新增的框架,是對 UIWebView的一次封裝,方便開發(fā)者使用,使用JavaScriptCore.framework可以輕松實現(xiàn) ios與js的交互

JavaScriptCore的組成

JavaScriptCore中主要的類


java.png

詳細介紹
1, JSContext --- 在OC中創(chuàng)建JavaScript運行的上下文環(huán)境

// 創(chuàng)建JSContext對象,獲得JavaScript運行的上下文環(huán)
- (instancetype)init; 
// 在特定的對象空間上創(chuàng)建JSContext對象,獲得JavaScript運行的上下文環(huán)境
- (instancetype)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine;
// 運行一段js代碼,輸出結(jié)果為JSValue類型
- (JSValue *)evaluateScript:(NSString *)script;
// iOS 8.0以后可以調(diào)用此方法
- (JSValue *)evaluateScript:(NSString *)script withSourceURL:(NSURL *)sourceURL NS_AVAILABLE(10_10, 8_0);
// 獲取當前正在運行的JavaScript上下文環(huán)境
+ (JSContext *)currentContext;
// 返回結(jié)果當前執(zhí)行的js函數(shù) function () { [native code] } 亏吝,iOS 8.0以后可以調(diào)用此方法
+ (JSValue *)currentCallee NS_AVAILABLE(10_10, 8_0);
// 返回結(jié)果當前方法的調(diào)用者[object Window]
+ (JSValue *)currentThis;
// 返回結(jié)果為當前被調(diào)用方法的參數(shù)
+ (NSArray *)currentArguments;
// js的全局變量 [object Window]
@property (readonly, strong) JSValue *globalObject;
// 返回js 調(diào)用時候的異常信息
@property (copy) void(^exceptionHandler)(JSContext *context, JSValue *exception);
//  異常捕獲中錯誤值處理
@property (strong) JSValue *exception;
// 上下文的名字
@property (copy) NSString *name NS_AVAILABLE(10_10, 8_0);

JSValue --- JavaScript中的變量和方法敞咧,可以轉(zhuǎn)成OC數(shù)據(jù)類型,每個JSValue都和JSContext相關(guān)聯(lián)并且強引用context
OC 和 js 數(shù)據(jù)對照表

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)
// 在context創(chuàng)建BOOL的JS變量
+ (JSValue *)valueWithBool:(BOOL)value inContext:(JSContext *)context;
// 修改JS對象的屬性的值
- (void)setValue:(id)value forProperty:(NSString *)property;
// 調(diào)用者JSValue對象為JS中的方法名稱,arguments為參數(shù),調(diào)用JS中Window直接調(diào)用的方法
- (JSValue *)callWithArguments:(NSArray *)arguments;
// 調(diào)用者JSValue對象為JS中的全局對象名稱鲁僚,method為全局對象的方法名稱旭斥,arguments為參數(shù)
- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments;
// JS中的結(jié)構(gòu)體類型轉(zhuǎn)換為OC
+ (JSValue *)valueWithPoint:(CGPoint)point inContext:(JSContext *)context;
// 將JS變量轉(zhuǎn)換成OC中的BOOL類型/提供了其他方法的轉(zhuǎn)換
- (BOOL)toBool;
 // JS中是否有這個對象
// @property (readonly) BOOL isUndefined;
// 比較兩個JS對象是否相等
- (BOOL)isEqualToObject:(id)value;

3, JSExport --- JS調(diào)用OC中的方法和屬性寫在繼承自JSExport的協(xié)議當中容达,OC對象實現(xiàn)自定義的協(xié)議

官方給的例子
//@textblock
    @protocol MyClassJavaScriptMethods <JSExport>
    - (void)foo;
    @end
  // 方法實現(xiàn)
    @interface MyClass : NSObject <MyClassJavaScriptMethods>
    - (void)foo;
    - (void)bar;
    @end
//@/textblock

4, JSManagedValue --- JS和OC對象的內(nèi)存管理輔助對象,主要用來保存JSValue對象,解決OC對象中存儲js的值,導(dǎo)致的循環(huán)引用問題

// 初始化
- (instancetype)initWithValue:(JSValue *)value;
+ (JSManagedValue *)managedValueWithValue:(JSValue *)value;
+ (JSManagedValue *)managedValueWithValue:(JSValue *)value andOwner:(id)owner NS_AVAILABLE(10_10, 8_0);

JSManagedValue本身只弱引用js值垂券,需要調(diào)用JSVirtualMachine的addManagedReference:withOwner:把它添加到JSVirtualMachine中董饰,這樣如果JavaScript能夠找到該JSValue的Objective-C owner,該JSValue的引用就不會被釋放圆米。

5, JSVirtualMachine --- JS運行的虛擬機卒暂,有獨立的堆空間和垃圾回收機制,運行在不同虛擬機環(huán)境的JSContext可以通過此類通信娄帖。

// 初始化
- (instancetype)init;
// 添加
- (void)addManagedReference:(id)object withOwner:(id)owner;
 //  移除
 - (void)removeManagedReference:(id)object withOwner:(id)owner;

到此 JavaScriptCore.framework 能夠使用的基本api已經(jīng)介紹完畢



下面我們以簡單集成SMSDK為 實際例子進行講解
首先創(chuàng)建必要的三個文件 SMSDK.html/ SMSDK.cc SMSDK.js 在viewDidLoad中創(chuàng)建webview進行加載SDMSDK.html

iOS調(diào)用 js

jsios.png

js中方法的實現(xiàn)

在 SMSDK.js中 創(chuàng)建 ios需要的初始化對象 并保留一個 對象接口  方便在 html中 調(diào)用這個方法
SMSDK.js
function SMSDK()
{
//    不能添加 alert() 等外部的方法
//    alert(--qq--);
    var name = "金山";
    this.initSDK = function (hello)
    {
        var initData ={};
        var appkey =
            {
            "appkey":"f3fc6baa9ac4"
            }
        var appSecrect=
            {
            "appSecrect":"7f3dedcb36d92deebcb373af921d635a"
            }
        initData["appkey"] = appkey;
        initData["appSecrect"] = appSecrect;
        return initData;
    };
//    必須使用this 關(guān)鍵字
}
var $smsdk = new SMSDK();
在 SMSDK.html中進行引用并 創(chuàng)建js 方法
         <script>
            function initSDK(hello)
            {
                return $smsdk.initSDK(hello);
            }
            </script>
            這里的 initSDK(); 方法就是留給ios 調(diào)用的方法

我們同樣選擇在網(wǎng)頁加載完成的方法中進行方法調(diào)用

-(void) webViewDidFinishLoad:(UIWebView *)webView
{
    [self initSMSDK];
}
實現(xiàn) 方法
-(void) initSMSDK
{
//    創(chuàng)建上下文
// 1.這種方式需要傳入一個JSVirtualMachine對象也祠,如果傳nil,會導(dǎo)致應(yīng)用崩潰的近速。
   JSVirtualMachine *JSVM = [[JSVirtualMachine alloc] init];
   JSContext *jscontext = [[JSContext alloc] initWithVirtualMachine:JSVM];
     2.這種方式诈嘿,內(nèi)部會自動創(chuàng)建一個JSVirtualMachine對象,可以通過JSCtx.virtualMachine
    // 看其是否創(chuàng)建了一個JSVirtualMachine對象削葱。
   JSContext *jscontext = [[JSContext alloc] init];
/**********以上的方法經(jīng)過測試都不好使*************/
   正確的姿勢 
1,   創(chuàng)建上下文,意思就是讓oc和js 同處于一個環(huán)境,方便進行方法調(diào)用
    JSContext *jscontext = [self.mywebview valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
2   參數(shù)接受  注意一定輸傳遞方法的名字,不要加()// 和webview的調(diào)用方法的區(qū)別
    JSValue *jsvalue = jscontext[@"initSDK"];   // 返回的是方法的名字和內(nèi)容
3  調(diào)用函數(shù)/傳遞參數(shù)
  JSValue *initData = [jsvalue callWithArguments:@[@"從oc中傳遞第一個參數(shù)進去"]];
  接收到的字典對象進行解析,并調(diào)用SMSDK的初始化方法 
  NSDictionary *dic = [initData toDictionary];
    NSString *appkey = dic[@"appkey"][@"appkey"];
    NSString  *appSecrect = dic[@"appSecrect"][@"appSecrect"];
    [SMSSDK registerApp:appkey withSecret:appSecrect];
}

我們通過打印上面的 appkey 等參數(shù)就知道 實現(xiàn)了ios調(diào)用 js的方法

js調(diào)用 ios

可以通過兩種方式在JavaScript中調(diào)用Objective-C:
Blocks: 對應(yīng)JS函數(shù)
JSExport協(xié)議: 對應(yīng)JS對象
我們先實現(xiàn) block的方法

Block的方法
首先在html中 寫一個button的點擊方法

 <button id="getCode" onclick="getCode($smsdk.getCode())">獲取手機號</button>

getCode()方法就是將來 ios 中需要注冊的方法體, 里面是對js返回參數(shù)方法的調(diào)用 相當于 getCode('返回給js的參數(shù),在參數(shù)列表中調(diào)用就可以');

過程和上面一樣 
// 1,獲取上下文  // 下文weakSelf.jsContext 中已經(jīng)在屬性中保存jscontext
JSContext *_jsContext = [self.mywebview valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
 // 2 異常捕獲機制
    _jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception) {
        context.exception = exception;
        //別忘了給exception賦值奖亚,否則JSContext的異常信息為空
        NSLog(@"---錯誤數(shù)據(jù)的處理----%@",exception);
    };
      __weak typeof (self) weakSelf = self; // 防止循環(huán)引用就加上 __weak
_jsContext[@"getCode"] = ^ (){

        NSArray *arr =[JSContext currentArguments]; // 獲取當前的上下文,然后加載 js返回的參數(shù)列表
        for (id objc in arr) {
            weakSelf.phoneNumber = objc;
            [SMSSDK getVerificationCodeByMethod:SMSGetCodeMethodSMS phoneNumber:objc zone:@"86" customIdentifier:nil result:^(NSError *error) {
                JSContext *jscontext = weakSelf.jsContext;
                if (!error)
                {
                //  ios回調(diào) js的代碼 并傳遞參數(shù)給 js
                    JSValue *jsvalue = jscontext[@"getCodeCallBack"]; // 注入方法
                  [jsvalue callWithArguments:@[@"獲取短信驗證碼成功"]];                               // 調(diào)用方法
                 }
                else
                {
                    JSValue *jsvalue = jscontext[@"getCodeCallBack"]; // 注入方法
                    [jsvalue callWithArguments:@[@"獲取驗證碼失敗"]];
                }
            }];
        }
    };

通過上面的方法我們知道, block 方法 ios 給 js傳遞的是方法 ,如果傳遞 對象則需要用到 協(xié)議的方法
首先我們來看一個最簡單的例子 :
在 js 上寫一個 點擊事件,然后調(diào)用 ios 的方法
在 js中

在 SMDK.html中添加 
<div>
    <input type= "button" width="50%" height="5%" id = "Button" value = "需要測試" onclick = "objc.takePicture()"></button>
</div>
說明:  這里的 objc 對象就是將來我們需要在 ios中注冊的對象, takePicture() ,就是在協(xié)議中的方法

在viewController.m中,定義協(xié)議

/**
 *  實現(xiàn)js代理,js調(diào)用ios的入口就在這里
 */
@protocol JSDelegate <JSExport>

- (void)getImage:(id)parameter;// 這個方法就是window.document.iosDelegate.getImage(JSON.stringify(parameter)); 中的 getImage()方法

@end

簽訂協(xié)議: 注意這里的協(xié)議不需要實現(xiàn),在外部也不需要設(shè)置代理,直接實現(xiàn)就可以

@interface ViewController ()<UIWebViewDelegate,JSDelegate,UINavigationControllerDelegate,UIImagePickerControllerDelegate,JSOCViewControllerExport>

在網(wǎng)頁加載完成的代理中實現(xiàn), 上下文

    self.jsContext = [self.myWebView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//    測試處理
    self.jsContext[@"objc"] = self;//掛上代理  iosDelegate是window.document.iosDelegate.getImage(JSON.stringify(parameter)); 中的 iosDelegate
    self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception){
        context.exception = exception;
        NSLog(@"js方法寫錯了 錯誤的信息都會在此處輸出:%@",exception);
    };

最后一步就是實現(xiàn) 在 js中聲明的協(xié)議方法

//  協(xié)議實現(xiàn)
- (void)getImage:(id)parameter
{
    NSArray *arr =[JSContext currentArguments];
    for (id objc in arr)
    {
        NSLog(@"=-----%@",[objc toDictionary]);
    }
    [self beginOpenPhoto];  // 相機的處理,
}

實現(xiàn)相機的方法,很簡單, 里面的數(shù)據(jù)傳遞和上文中說到的一個樣子,這里將不再贅述.
到此, 利用協(xié)議實現(xiàn) js 調(diào)用 ios的方法基本完成

關(guān)于內(nèi)存泄漏,循環(huán)引用的問題 注意不要在 block中直接 引用外部 強引用的對象就可以

 __weak typeof (self) weakSelf = self; // 防止循環(huán)引用就加上 __weak
    _jsContext[@"getCode"] = ^ (id oc){
        NSArray *arr =[JSContext currentArguments]; // 獲取當前的上下文
        for (id objc in arr) {
            weakSelf.phoneNumber = objc;
這個  weakSelf 的處理就ok

githubDemo:https://github.com/wangjinshan/JSCDemo


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末析砸,一起剝皮案震驚了整個濱河市昔字,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌首繁,老刑警劉巖作郭,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異弦疮,居然都是意外死亡夹攒,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門胁塞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咏尝,“玉大人,你說我怎么就攤上這事啸罢”嗉欤” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵伺糠,是天一觀的道長蒙谓。 經(jīng)常有香客問我,道長训桶,這世上最難降的妖魔是什么累驮? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任酣倾,我火速辦了婚禮,結(jié)果婚禮上谤专,老公的妹妹穿的比我還像新娘躁锡。我一直安慰自己,他們只是感情好置侍,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布映之。 她就那樣靜靜地躺著,像睡著了一般蜡坊。 火紅的嫁衣襯著肌膚如雪杠输。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天秕衙,我揣著相機與錄音蠢甲,去河邊找鬼。 笑死据忘,一個胖子當著我的面吹牛鹦牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播勇吊,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼曼追,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了汉规?” 一聲冷哼從身側(cè)響起礼殊,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鲫忍,沒想到半個月后膏燕,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡悟民,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了篷就。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片射亏。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖竭业,靈堂內(nèi)的尸體忽然破棺而出智润,到底是詐尸還是另有隱情,我是刑警寧澤未辆,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布窟绷,位于F島的核電站,受9級特大地震影響咐柜,放射性物質(zhì)發(fā)生泄漏兼蜈。R本人自食惡果不足惜攘残,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望为狸。 院中可真熱鬧歼郭,春花似錦、人聲如沸辐棒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽漾根。三九已至泰涂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間辐怕,已是汗流浹背负敏。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留秘蛇,地道東北人其做。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像赁还,于是被迫代替她去往敵國和親妖泄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容