JSPatch 學(xué)習(xí) -- 基礎(chǔ)原理分析

?JSPatch是一個(gè) iOS 動(dòng)態(tài)更新框架梧喷,靈動(dòng)輕巧砌左,不用更新版本、不用更改原生代碼铺敌,就能達(dá)到更新的效果

目錄

1.JSPatch如何使用
2.J?SPatch基礎(chǔ)原理
3.JSPatch使用注意事項(xiàng)汇歹、深究、建議
4.參與文獻(xiàn)

一偿凭、JSPatch如何使用

只需要在項(xiàng)目中引入JSPatch产弹,在AppDelegate.m里面引入JSPatch庫,依賴框架JavaScriptCore.framework弯囊。具體操作可見JSPatch平臺(tái)痰哨,也可以去github上下載源碼

JSPatch平臺(tái)
JSPatch github

1.如果將可更新的代碼及js文件放在自己的后臺(tái)

如果用自己的后臺(tái)就得考慮很多因素,這里我會(huì)大概寫下流程注意事項(xiàng)匾嘱,截圖為已經(jīng)從后臺(tái)安全的下載下來并且保存為demo.js了斤斧,在項(xiàng)目中使用這個(gè)demo.js。一般在程序入口頁使用霎烙,當(dāng)然如果要求實(shí)時(shí)性特別強(qiáng)的話可以在喚醒程序處使用該段代碼

    [JPEngine startEngine];
    NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"];
    NSString *script = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil];
    [JPEngine evaluateScript:script];
下載流程

整個(gè)流程需要考慮到安全性(js權(quán)限大)撬讽、數(shù)據(jù)完整性(不完整會(huì)導(dǎo)致程序崩潰蕊连,后來作者采用release版不崩潰,只是js不執(zhí)行)锐秦、分支管理問題(一個(gè)版本多個(gè)patch、多個(gè)版本多個(gè)patch盗忱、保證老版本兼容)酱床、線程處理(切不可堵塞主線程)。整個(gè)流程應(yīng)保持加密傳輸趟佃,加密保存扇谣,可用md5完整性處理,分配多個(gè)分支闲昭。

2.使用JSPatch作者自己寫的平臺(tái)

JSPatch的作者創(chuàng)建了一個(gè)平臺(tái)罐寨,如其他SDK平臺(tái)一樣,介紹了包括文檔/費(fèi)用/工具/sdk下載/上傳js文件序矩。使用appid保持項(xiàng)目唯一性鸯绿,可建立多個(gè)app,建立多個(gè)版本簸淀,上傳多個(gè)js文件瓶蝴。整個(gè)下載流程都已處理好,如果要求安全性租幕,可生成RSA密鑰舷手。甚至還提供灰度下發(fā)和條件下發(fā)等功能。簡捷易用劲绪。

#if DEBUG  //在debug模式下進(jìn)行測試男窟,將js放在項(xiàng)目中
    [JSPatch testScriptInBundle];
#else
        //可以設(shè)置過濾條件,進(jìn)行灰度下發(fā)贾富,可不設(shè)
        NSMutableDictionary *filter = [[NSMutableDictionary alloc] init];
        [filter setObject:@"mobileNo" forKey:@"mobile"];
        [filter setObject:@"uuid" forKey:@"identifier"];
        [JSPatch setupUserData:filter];
       //可設(shè)置rsa密鑰歉眷,可不設(shè),若設(shè)了颤枪,得傳到平臺(tái)上
        [JSPatch setupRSAPublicKey:@"-----BEGIN PUBLIC KEY----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4UKDS8kcwdG8TU2UpsRr4+9ECAwEAAQ==\n-----END PUBLIC KEY-----"];
       //appkey為在平臺(tái)上申請得到的值
        [JSPatch startWithAppKey:@"3339203202"];
        [JSPatch sync];
    }

JSPatch基礎(chǔ)原理

JSPatch怎么將js代碼轉(zhuǎn)換為oc中可執(zhí)行的代碼

利用JavaScriptCore.framework庫中的JSContext來執(zhí)行js代碼,可下載demoJSPatch demo

  1. JSContext執(zhí)行js代碼 - 基本類型的轉(zhuǎn)換
JSContext *context = [[JSContext alloc] init];
    //執(zhí)行 js 代碼  @"var arr = [21, 7.0, 'aaa.com'];"
    [context evaluateScript:@"var arr = [21, 7.0, 'aaa.com'];"];
    JSValue *jsArr = context[@"arr"]; // Get array from JSContext
    NSLog(@"JS Array: %@; OCArray : %@; Length: %@", jsArr,[jsArr toArray],jsArr[@"length"]);
    jsArr[1] = @"blog"; // Use JSValue as array
    jsArr[7] = @7;
    
    NSLog(@"JS Array: %@; OCArray : %@; Length: %@", jsArr,[jsArr toArray],jsArr[@"length"]);

//打印結(jié)果 : JS Array: 21,7,aaa.com; OCArray : (21,7, "aaa.com");Length: 3
//JS Array: 21,blog,aaa.com,,,,,7; OCArray : ( 21,blog,"aaa.com","<null>", "<null>", "<null>", "<null>",7); Length: 8

2.JSContext執(zhí)行js代碼 - js代碼調(diào)用oc方法

JSContext *context = [[JSContext alloc] init];
    context[@"log"] = ^() {
        NSLog(@"+++++++Begin Log+++++++");
        
        NSArray *args = [JSContext currentArguments];
        
        for (JSValue *jsVal in args) {
            NSLog(@"jsVal %@", jsVal);
        }
        
        for (int i = 0; i<args.count; i++) {

            if (i == 1) {
                JSValue *twoObject = args[1];
                NSLog(@"OC twoobject = %@",[twoObject toArray]);
            }
            if (i == 2) {
                JSValue *twoObject = args[2];
                NSLog(@"OC threeobject = %@",[twoObject toDictionary]);
            }
        }
        JSValue *this = [JSContext currentThis];
        NSLog(@"this: %@",this);
        NSLog(@"-------End Log-------");
    };
    [context evaluateScript:@"log('ider', [7, 21], { hello:'world', js:100});"];

//打印結(jié)果:+++++++Begin Log+++++++   
// jsVal ider   jsVal 7,21  jsVal [object Object]  
// OC twoobject = (  7, 21)  OC threeobject = {hello = world; js = 100;}   this: [object GlobalObject] 
// -------End Log-------

3.oc調(diào)用js中的方法

//JSValue沒有toBlock方法來把JavaScript方法變成Block在Objetive-C中使用姥芥,但可以用以下方法來實(shí)現(xiàn)oc調(diào)用js方法
JSContext *context = [[JSContext alloc] init];
    [context evaluateScript:@"function add(a, b) { return a + b; }"];
    JSValue *add = context[@"add"];
    NSLog(@"Func: %@", add);
    
    JSValue *sum = [add callWithArguments:@[@(7), @(21)]];
    NSLog(@"Sum: %d",[sum toInt32]);
// 打印結(jié)果:Func: function add(a, b) { return a + b; }
// Sum: 28

綜上所述,JSPatch中就是以以上形式來調(diào)用oc方法汇鞭,將要替換或新增的類名凉唐、方法名、參數(shù)霍骄、實(shí)現(xiàn)傳給oc台囱,oc再通過拿到的類等參數(shù),找到這方法沒有就新增读整,替換原有實(shí)現(xiàn)簿训。流程見下圖

D2F91739-C4BF-4F87-B001-7DDB867910BB.png

JSPatch怎么將js的實(shí)現(xiàn)替換成oc中原有缺陷的實(shí)現(xiàn)

利用runtime - swizzle 簡稱黑盒子方法,將方法實(shí)現(xiàn)指針替換,講runtime的文章很多强品,在這里不細(xì)談膘侮,添下實(shí)現(xiàn),也可下載demo runtime demo

D853B9CF-980F-4F7B-A798-B9999FD330B8.png

+ (void)load{
    
    SEL originSelector = @selector(viewDidLoad);
    SEL swizzleSelector = @selector(myViewDidLoad);
    //得到viewDidLoad方法的函數(shù)指針
    Method originalMethod = class_getInstanceMethod(self, originSelector);
    //得到myViewDidLoad方法的函數(shù)指針
    Method swizzledMethod = class_getInstanceMethod(self, swizzleSelector);
    //新增一個(gè)originSelector方法,指向原來viewDidLoad實(shí)現(xiàn)
    if (class_addMethod(self, originSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {
        class_replaceMethod(self, swizzleSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

這樣一執(zhí)行viewDidLoad方法的榛,就會(huì)走到myViewDidLoad方法實(shí)現(xiàn)中琼了,達(dá)到替換的效果,JSPatch也正是利用此方法夫晌,通過js代碼傳過來的類名和方法名及參數(shù)雕薪,找到這個(gè)類的這個(gè)方法(沒有就新增),替換其實(shí)現(xiàn)指針晓淀,達(dá)到替換的效果所袁,但這里作者并不是直接將要修復(fù)的方法替換成js中的新實(shí)現(xiàn),而是全部替換到消息轉(zhuǎn)發(fā)機(jī)制凶掰,這樣參數(shù)處理問題就變的容易燥爷,因?yàn)镹SInvocation 對象里拿到調(diào)用的所有參數(shù)值。拿到之后再通過方法名和JSContext中的調(diào)用js中實(shí)現(xiàn)的方法一步步調(diào)用其js實(shí)現(xiàn)懦窘。達(dá)到修復(fù)效果

一局劲、JSPatch使用注意事項(xiàng)、深究奶赠、建議

注意事項(xiàng)

1.使用CGRect鱼填、枚舉值時(shí)一定要注意,js中不直接支持毅戈,可以下方式書寫,更多注意事項(xiàng)多看看作者文檔就好了苹丸,不會(huì)的百度,一般都會(huì)寫出

var screenBounds = UIScreen.mainScreen().bounds();
var btnFrame = {x:0, y:200, width:screenBounds.width, height:50};
var btn = UIButton.alloc().initWithFrame(btnFrame);
 btn.setTitle_forState("Push LCJTableViewController", 0);
 btn.addTarget_action_forControlEvents(self, 'handleBtn:', 1111111);

2.?使用SDK時(shí)若用模擬器測試苇经,一定記得打開 Capabilities -> keychainsharing(不然老下載不成功)

可深究

1.可深究作者對于OC中可變數(shù)組赘理、可變字典等 和 js中的數(shù)組(js中只有Array類型) 轉(zhuǎn)換是怎么處理的
2.OC中的NSNull nill Null 和 js中的 null / undefined的轉(zhuǎn)換。
3.JSPatch庫處于開發(fā)中扇单,暫不支持CG庫商模,可自己深究。等等等等

建議

就一個(gè)蜘澜,不要灰心施流,多考慮多看,最重要的是使用調(diào)試工具(可聯(lián)合xcode斷點(diǎn))一步步的調(diào)試了解其流程鄙信。JS斷點(diǎn)調(diào)試(我就是從一個(gè)接到任務(wù)嚇壞了的人慢慢的變成了將其用在項(xiàng)目中)瞪醋。加油,希望該文章對您有所幫助

四装诡、參與文獻(xiàn)

感謝JSPatch作者和提供以下資料的辛勤者
JSPatch SDK接入平臺(tái)
JSPatch github地址
JSPatch 實(shí)現(xiàn)原理
JSPatch OC轉(zhuǎn)化為滿足條件的JS代碼
runtime - swizzle
JavaScriptCore框架入門介紹

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末银受,一起剝皮案震驚了整個(gè)濱河市践盼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宾巍,老刑警劉巖咕幻,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異顶霞,居然都是意外死亡肄程,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門确丢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绷耍,“玉大人吐限,你說我怎么就攤上這事鲜侥。” “怎么了诸典?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵描函,是天一觀的道長。 經(jīng)常有香客問我狐粱,道長舀寓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任肌蜻,我火速辦了婚禮互墓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蒋搜。我一直安慰自己篡撵,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布豆挽。 她就那樣靜靜地躺著育谬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪帮哈。 梳的紋絲不亂的頭發(fā)上膛檀,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機(jī)與錄音娘侍,去河邊找鬼咖刃。 笑死,一個(gè)胖子當(dāng)著我的面吹牛憾筏,可吹牛的內(nèi)容都是我干的僵缺。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼踩叭,長吁一口氣:“原來是場噩夢啊……” “哼磕潮!你這毒婦竟也來了翠胰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤自脯,失蹤者是張志新(化名)和其女友劉穎之景,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體膏潮,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡锻狗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了焕参。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片轻纪。...
    茶點(diǎn)故事閱讀 40,675評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖叠纷,靈堂內(nèi)的尸體忽然破棺而出刻帚,到底是詐尸還是另有隱情,我是刑警寧澤涩嚣,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布崇众,位于F島的核電站,受9級(jí)特大地震影響航厚,放射性物質(zhì)發(fā)生泄漏顷歌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一幔睬、第九天 我趴在偏房一處隱蔽的房頂上張望眯漩。 院中可真熱鬧,春花似錦麻顶、人聲如沸赦抖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽摹芙。三九已至,卻和暖如春宛瞄,著一層夾襖步出監(jiān)牢的瞬間浮禾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工份汗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盈电,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓杯活,卻偏偏與公主長得像匆帚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子旁钧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評論 2 360

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