OC和JS調(diào)用

一:webview:互調(diào)底層用的JavaScriptCore

js調(diào)用oc方法,然后把方法結(jié)果返回給js

例如:用戶在網(wǎng)頁做了某個點(diǎn)擊操作,網(wǎng)頁需要從客戶端獲取該用戶信息(用戶ID拨匆,設(shè)備ID,系統(tǒng)版本等)

1、客戶端和 web端定義好統(tǒng)一的偽協(xié)議

  例如需要獲取用戶信息:tcan://getUserInfo?callback=window.getUserInfoResult

1.1藤巢、tcan://只是定義的一個前綴,可以用不一樣的前綴來區(qū)分不同的模塊(看業(yè)務(wù)需求來定息罗,也可以不區(qū)分)

1.2掂咒、getUserInfo 用來區(qū)分不同的操作

1.3、 callback=window.getUserInfoResult 表明可通過callback來獲取需要執(zhí)行的js方法名

2、js請求 oc:

在網(wǎng)頁點(diǎn)擊等行為的時候绍刮,js操作跳轉(zhuǎn):

元素.onclick = function( ) {
    window.location.href = ‘tcan://getUserInfo?callback=window.getUserInfoResult';
}

3温圆、oc響應(yīng)請求并按需調(diào)用 js:stringByEvaluatingJavaScriptFromString

3.1、客戶端在UIWebViewDelegate方法 - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
中攔截加載孩革。

//獲取加載的URL:
NSURL *requestURL = request.URL;

//判斷頭部廉油,區(qū)分不同模塊的邏輯:
 if ([requestURL.absoluteString hasPrefix:@"tcan://"]) {

    //獲取host
    NSString *host = requestURL.host;

    //根據(jù)不同的host做不同的響應(yīng)
    if ([host isEqualToString:@"getUserInfo"]) {

       //獲取js回調(diào)函數(shù)名
       NSString *jsCallbackFunc = [parseObject.url parameterForKey:@"callback”];

            if (![jsCallbackFunc isUndefine]) {
                //設(shè)置要給js傳的參數(shù)
                NSDictionary *postParameter = [NSDictionary dictionaryWithObjectsAndKeys:@"用戶ID", @"user_id",
                                                    @"設(shè)備ID", @"uid", nil];

                //字典轉(zhuǎn)成json字符串(這里用了JSONKit),可以對數(shù)據(jù)進(jìn)行進(jìn)一步加鹽加密寞焙,例如AES-256
                NSString *postParameterString = [postParameter JSONString];
                //拼接js函數(shù)
                NSString *js_excmd = [NSString stringWithFormat:@"%@('%@')", jsCallbackFunc, postParameterString];
                //執(zhí)行
                [webview stringByEvaluatingJavaScriptFromString:js_excmd];
                  }
    }else if([host isEqualToString:@“xxx"]) {

    }else {

    }

}else if (xxx) {

    //另一套邏輯

}

二:蘋果原生API:JavaScriptCore (iOS7.0+ 使用)

主要的兩個類:

  • JSContext: JS上下文(運(yùn)行環(huán)境)吕粗,可用對象方法去執(zhí)行JS代碼(evaluateScript),可通過上下文對象去獲取JS里的數(shù)據(jù)(上下文對象[key])饱搏,并用JSValue對象接收
  • JSValue: 用于接收J(rèn)SContext對象獲取的數(shù)據(jù)非剃,可以是任意對象,方法推沸。

注:JSValue對其對應(yīng)的JS值和其所屬的JSContext對象都是強(qiáng)引用的關(guān)系备绽。因?yàn)镴SValue需要這兩個東西來執(zhí)行JS代碼,所以JSValue會一直持有著它們鬓催。

所以肺素,在用block時,需考慮循環(huán)引用問題

1宇驾、不要在Block中直接使用JSValue:建議把JSValue當(dāng)做參數(shù)傳到Block中倍靡,而不是直接在Block內(nèi)部使用,這樣Block就不會強(qiáng)引用JSValue了
2飞苇、不要在Block中直接使用JSContext:可以使用[JSContext currentContext] 方法來獲取當(dāng)前的Context

類型對應(yīng)關(guān)系:

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)

//------------------OC調(diào)用JS------------------//

oc獲取js變量菌瘫,注:js代碼要先被執(zhí)行,才能通過上下文獲取

- (void)ocGetJSVar
{
//定義JS代碼
NSString *jsCode = @"var a = 'a'";

//創(chuàng)建JS運(yùn)行環(huán)境
JSContext *ctx = [[JSContext alloc] init];

//!!!:執(zhí)行JS代碼---先執(zhí)行布卡,后面才能獲取
[ctx evaluateScript:jsCode];

//獲取變量
JSValue *value = ctx[**@“a"**];

//JSValue轉(zhuǎn)NSString
NSString *valueStr = value.toString;

//打印結(jié)果:a
NSLog(@"%@",valueStr);
}

oc調(diào)用js方法雨让,并獲取返回結(jié)果

- (void)ocCallJSFunc
{
NSString *jsCode = @"function say(str){"
" return str; "
"}";

// 創(chuàng)建JS運(yùn)行環(huán)境
JSContext *ctx = [[JSContext alloc] init];

// 執(zhí)行JS代碼
[ctx evaluateScript:jsCode];

//!!!:執(zhí)行JS代碼---先執(zhí)行,后面才能獲取
JSValue *say = ctx[@"say"];

// OC調(diào)用JS方法忿等,獲取方法返回值
JSValue *result = [say callWithArguments:@[@"hello world!"]];

// 打印結(jié)果:hello world!
NSLog(@"%@",result);
}

//------------------JS調(diào)用OC—————————//

1栖忠、block方式:

用block定義js函數(shù),并執(zhí)行(OC調(diào)用執(zhí)行js贸街,而js是調(diào)用的oc block)

- (void)jsCallOCBlock
{

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

//OC中NSBlock對應(yīng)js中Function object
ctx[@"goto"] = ^(NSString *parmStr){

   //block內(nèi)不要直接使用ctx庵寞,會循環(huán)引用(ctx已經(jīng)引用block),若外部有JSValue薛匪,也不能在block內(nèi)直接調(diào)用(JSValue強(qiáng)持有了ctx )

    //獲取JS調(diào)用參數(shù)
    NSLog(@"parmStr:%@",parmStr);

    //可以直接獲取所有參數(shù)
    NSArray *arguments = [JSContext currentArguments];
    NSLog(@"%@",arguments[0]);
};

//JS執(zhí)行代碼,調(diào)用goto方法捐川,并傳入?yún)?shù)school
NSString *jsCode = @"goto('school')";

//執(zhí)行
[ctx evaluateScript:jsCode];
}

2、JSExport 協(xié)議

需要在JS中生成OC對應(yīng)的類逸尖,然后再通過JS調(diào)用古沥。

方法:
通過自定義一個遵循JSExport的協(xié)議瘸右,把需要被JS訪問的OC類中的屬性,方法暴露給JS使用

步驟:
1岩齿、自定義協(xié)議太颤,協(xié)議遵循JSExport的協(xié)議,協(xié)議中的屬性和方法就是OC對象要暴露給JS盹沈,讓JS可以直接調(diào)用的

例如:

#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>

@protocol PersonJSExport <JSExport>

@property (nonatomic, strong) NSString *address;

//無參數(shù)方法
- (void)play;

//因?yàn)镴S函數(shù)命名規(guī)則和OC規(guī)則不一樣龄章,所以當(dāng)有多個參數(shù)時,可以使用OC提供了一個宏*JSExportAs*乞封,指定JS應(yīng)該生成什么樣的函數(shù)來對應(yīng)OC的方法做裙。
//不使用JSExportAs指定關(guān)聯(lián)也可以正常調(diào)用,后面直接接多個參數(shù)

//多參數(shù)方法

//不指定關(guān)聯(lián)
- (void)play:(NSString *)address time:(NSString *)time;
//指定關(guān)聯(lián)
JSExportAs(gotoSchool, - (void)goToSchoolWithSchoolName:(NSString *)name address:(NSString *)address);

@end

2肃晚、自定義一個遵循第一步創(chuàng)建的協(xié)議的OC對象菇用,實(shí)現(xiàn)協(xié)議的方法

例如:Person對象

3、JS調(diào)用對象協(xié)議聲明的方法

- (void)jsCallOCClass
{
// 創(chuàng)建Person對象,Person對象必須遵守JSExport協(xié)議
Person *p = [[Person alloc] init];
p.address = @"aaa";

JSContext *ctx = [[JSContext alloc] init];
// 在JS中生成Person對象person陷揪,并且擁有p內(nèi)部的值
ctx[@"person"] = p;

// 執(zhí)行JS代碼
//NSString *jsCode = @"person.play()";
NSString *jsCode = @"person.play('北京天安門','now')";
//NSString *jsCode = @"person.gotoSchool('實(shí)驗(yàn)中學(xué)','廣州')";

[ctx evaluateScript:jsCode];
}

另外,若要調(diào)用OC系統(tǒng)的類杂穷,例如UIView
需要同樣創(chuàng)建協(xié)議悍缠,只是在第三步用runtime給系統(tǒng)類UIView添加創(chuàng)建的協(xié)議
class_addProtocol([UIView class], @protocol(UIViewlJSExport));

JavaScriptCore參考:深入淺出 JavaScriptCore


三:第三方框架:WebViewJavaScripteBridge

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市耐量,隨后出現(xiàn)的幾起案子飞蚓,更是在濱河造成了極大的恐慌,老刑警劉巖廊蜒,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趴拧,死亡現(xiàn)場離奇詭異,居然都是意外死亡山叮,警方通過查閱死者的電腦和手機(jī)著榴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屁倔,“玉大人脑又,你說我怎么就攤上這事∪窠瑁” “怎么了问麸?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長钞翔。 經(jīng)常有香客問我严卖,道長,這世上最難降的妖魔是什么布轿? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任哮笆,我火速辦了婚禮来颤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘疟呐。我一直安慰自己脚曾,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布启具。 她就那樣靜靜地躺著本讥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鲁冯。 梳的紋絲不亂的頭發(fā)上拷沸,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天,我揣著相機(jī)與錄音薯演,去河邊找鬼撞芍。 笑死,一個胖子當(dāng)著我的面吹牛跨扮,可吹牛的內(nèi)容都是我干的序无。 我是一名探鬼主播,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼衡创,長吁一口氣:“原來是場噩夢啊……” “哼帝嗡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起璃氢,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤哟玷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后一也,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體巢寡,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年椰苟,在試婚紗的時候發(fā)現(xiàn)自己被綠了抑月。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡尊剔,死狀恐怖爪幻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情须误,我是刑警寧澤挨稿,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站京痢,受9級特大地震影響奶甘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜祭椰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一臭家、第九天 我趴在偏房一處隱蔽的房頂上張望疲陕。 院中可真熱鬧,春花似錦钉赁、人聲如沸蹄殃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽诅岩。三九已至,卻和暖如春带膜,著一層夾襖步出監(jiān)牢的瞬間吩谦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工膝藕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留式廷,地道東北人。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓芭挽,卻偏偏與公主長得像滑废,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子袜爪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評論 2 349

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