淺析JSPatch的使用

淺析JSPatch的使用

一. 背景介紹

  • 背景:iOS作為蘋果獨(dú)家開發(fā)和運(yùn)營的生態(tài)圈村生,具有非常封閉的運(yùn)作環(huán)境冰木,其App上線需要通過提交沿量,排隊浪慌,審核,上線這四個很長的過程朴则,過往的排隊時間長至于7~15天权纤,對一個軟件迭代就有了極大的限制,如果產(chǎn)品中出現(xiàn)了小bug, 需要修復(fù)問題的話就是及其困難的了乌妒,雖然目前蘋果的審核時間已經(jīng)短至1天汹想,但是其App更新方式為全量更新,如若為了修復(fù)一個極小的問題而強(qiáng)迫用戶更新一個版本的話撤蚊,體驗也是不好的古掏, 所以在多年的技術(shù)進(jìn)化中,產(chǎn)生了App熱更新這樣的一個需求侦啸。

  • 發(fā)展: 在這幾年來的iOS開發(fā)界槽唾,各個廠商和各種極客各現(xiàn)神通,通過各種黑科技來實現(xiàn)各種熱更新技術(shù)的實現(xiàn)光涂,在iOS7之前庞萍,著名的有Wax框架,Wax框架主要使用Lua語言進(jìn)行腳本實現(xiàn)忘闻,并且需要在原生的App中植入Wax的腳本引擎钝计,使用上也不是太方便,所以逐漸被淘汰。2013年私恬,蘋果在iOS7中引入了原生框架JavascriptCore這樣一個原生框架债沮,徹底打開了JS腳本和native調(diào)用之間的橋梁,也為熱更新技術(shù)的實現(xiàn)提供了原生的技術(shù)支持本鸣,各種極客和軟件開發(fā)商都定制自己的腳本來調(diào)用本地的部分代碼疫衩,但是都沒有統(tǒng)一的方案,在這一個沉淀的過程中永高,產(chǎn)生了較為成熟易用的JSPatch框架隧土。

二. JSPatch介紹

  • 誕生:JSPatch誕生于2015年5月提针,最初出自于騰訊廣研院的高級iOS開發(fā)工程師@bang的個人項目命爬,其超級深厚的開發(fā)基礎(chǔ)和過人的天賦在這個項目中表現(xiàn)的淋漓盡致,其基本原理為使用javascriptcore中提供的調(diào)用Objective-C的原生入口辐脖,并充分利用Objective-C的動態(tài)特點(diǎn)來在運(yùn)行時修改方法的入口和實現(xiàn)饲宛,用于替換原有的舊方法。 目前JSPatch在Github上已經(jīng)開源嗜价,擁有大量的擁簇艇抠,并且充分在騰訊,阿里久锥,百度各個大廠的產(chǎn)品中得到了驗證家淤,騰訊甚至提供了JSPatch托管SDK平臺用于商用,可見其實現(xiàn)已經(jīng)成熟到了商用的地步瑟由,可以放心使用了絮重。以下為其他開發(fā)者總結(jié)的JSPatch和Wax的區(qū)別:
JSPatch_Wax.jpg

基本原理

  • Objective-C是動態(tài)語言,具備運(yùn)行時特性歹苦,所以能夠在運(yùn)行時通過類名稱或者方法的名稱來獲取執(zhí)行入口青伤,并且進(jìn)行實際調(diào)用,而且還可以通過runtime特性來swizzle各種方法殴瘦,進(jìn)行動態(tài)修改狠角。
  • 通過類和方法名稱進(jìn)行運(yùn)行時調(diào)用的片段:
Class class = NSClassFromString(@"UIViewController");        
id viewController = [[class alloc] init];
SEL selector = NSSelectorFromString(@"viewDidLoad");
[viewController performSelector:selector];

動態(tài)替換類方法為新方法的片段:

Class cls = objc_allocateClassPair(superCls, "JPObject", 0);
objc_registerClassPair(cls);
class_addMethod(cls, selector, implement, typedesc);

正是由于OC語言具有如上片段展示的runtime時決定調(diào)用方法的特性,成就了熱更新的基礎(chǔ)原理蚪腋。

具體的實現(xiàn)原理比較復(fù)雜丰歌,作者已經(jīng)開源了代碼,并且在博客中寫明了實現(xiàn)過程中的各種問題屉凯,如果有興趣的可以參閱以下鏈接:
JSPatch在github的源地址,
JSPatch詳細(xì)實現(xiàn)原理

三. 使用方法

  • 基本語法解析

下面展示一下OC代碼和熱更新的JS腳本的對應(yīng)代碼片段立帖,例如線上 APP 有一段代碼出現(xiàn) bug 導(dǎo)致 crash:

@implementation JPTableViewController
...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
  NSString *content = self.dataSource[[indexPath row]];  //可能會超出數(shù)組范圍導(dǎo)致crash
  JPViewController *ctrl = [[JPViewController alloc] initWithContent:content];
  [self.navigationController pushViewController:ctrl];
}
...
@end

可以通過下發(fā)這樣一段 JS 代碼,覆蓋掉原方法神得,修復(fù)這個 bug:

//JS
defineClass("JPTableViewController", {
  //instance method definitions
  tableView_didSelectRowAtIndexPath: function(tableView, indexPath) {
    var row = indexPath.row()
    if (self.dataSource().length > row) {  //加上判斷越界的邏輯
      var content = self.dataArr()[row];
      var ctrl = JPViewController.alloc().initWithContent(content);
      self.navigationController().pushViewController(ctrl);
    }
  }
}, {})

除了修復(fù)一些bug之外厘惦,也可以動態(tài)的修改一些App的行為, 詳細(xì)越發(fā)可以查閱
JSPatch語法WIKI

  • SDK接入使用

前面已經(jīng)提到,騰訊已經(jīng)將JSPatch進(jìn)行了商用包裝宵蕉,使用方式非常簡單酝静,進(jìn)入JSPatch的官方站點(diǎn),下載SDK羡玛,將SDK拖入工程别智,然后添加官方j(luò)avascriptcore.framework就可以按照文檔進(jìn)行使用了,騰訊官方還進(jìn)行了后端平臺托管稼稿,使用者在后端注冊App獲取Key薄榛,然后在App端初始化即可使用,需要熱更新的腳本也配置在后端進(jìn)行下發(fā)让歼,使用非常方便簡潔敞恋,該平臺提供每天1W次下發(fā)的免費(fèi)量,超過1W次需要收費(fèi)服務(wù)谋右,詳細(xì)使用方式可以查閱:
JSPatch官方網(wǎng)站以及介紹

  • 源代碼引入使用

首先構(gòu)建一個簡單的demo, demo潔面只有一個按鈕硬猫,按鈕下面一個Label框,demo的簡單邏輯是改执,點(diǎn)擊按鈕之后啸蜜,Label框背景顏色變?yōu)樗{(lán)色,并且顯示native code字樣辈挂。

原始代碼片段如下:

demoSource.png

初始運(yùn)行界面為:

demoOne.png

點(diǎn)擊changeColor按鈕之后衬横,事件響應(yīng)并執(zhí)行,運(yùn)行界面變?yōu)椋?/p>

demoBlue.png

以上為native代碼的原始邏輯终蒂,下面我們開始進(jìn)行動態(tài)替換:

  • 思路

我們要動態(tài)替換changeColor的點(diǎn)擊事件蜂林,其實就是需要動態(tài)替換clickChangeColorEvent這個函數(shù),大家可以去按照上面的JSPatch的語法wiki中去查閱詳細(xì)語法后豫,我們這里有更簡單直接的方法悉尾,JSPatch的作者為了使用者方便,開發(fā)了
JSPatchConverter這個工具挫酿,大家可以去詳細(xì)看一下使用构眯,也可以直接下載成品App, 我們這里使用成品App進(jìn)行JS代碼生成,只需要在App的左側(cè)寫入要替換的方法早龟,點(diǎn)擊convert按鈕惫霸,右邊窗口即可生成熱更新的腳本代碼,如圖:

jsconverter.png

將右邊窗口的成品JS片段自己保留出來葱弟,然后放在自建的服務(wù)器準(zhǔn)備下發(fā)使用壹店。

  • 本地工程準(zhǔn)備

從github中下載JSPatch的源碼,并在源碼工程中找到JSPatch文件夾芝加,將該文件夾拖入demo工程硅卢,如圖:

treeOne.png

同時添加蘋果官方的javascriptcore.framework,如圖:

treeTwo.png
  • 代碼實現(xiàn)

首先要在AppDelegate.h中添加對應(yīng)的頭文件:

#import "JSPatch/JPEngine.h"

繼而在iOS的App啟動入口函數(shù)中添加JSEngine的初始化:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    [JPEngine startEngine];
    
    return YES;
}

以上兩步已經(jīng)完成了JSEngine的簡單的初始化過程,然后進(jìn)入下一步,獲取遠(yuǎn)端腳本的過程将塑。 將前面在JSConverter中生成的腳本代碼自己放入自建服務(wù)器中(這一步自己尋找后端兄弟協(xié)助完成), 然后在實現(xiàn)一個獲取腳本的函數(shù)脉顿,進(jìn)行腳本獲取,并將執(zhí)行獲取腳本的函數(shù)放在[JPEngine startEngine]之后執(zhí)行点寥,然后在獲取到腳本之后艾疟,執(zhí)行腳本行為,以下為完整范例:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    [JPEngine startEngine];
    [self getAndRunJSPatchScript];
    return YES;
}

- (void)getAndRunJSPatchScript
{
    NSURLSession *urlSession = [NSURLSession sharedSession];
    NSMutableURLRequest *httpRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://xxxxx/api"]];
    [httpRequest setHTTPMethod:@"POST"];
    NSURLSessionDataTask *dataTask = [urlSession dataTaskWithRequest:httpRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if(!error)
        {
            NSString *targetJSPatchScript = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            [JPEngine evaluateScript:targetJSPatchScript];
        }
    }];
    [dataTask resume];
}

然后運(yùn)行新的程序敢辩,點(diǎn)擊changeColor按鈕蔽莱,行為已經(jīng)發(fā)生了改變:

demoYellow.png

可以看到,圖中的色塊已經(jīng)變成了黃色戚长,圖中的提示語言也顯示JSPatch代碼進(jìn)行了執(zhí)行盗冷,而這一個過程當(dāng)中,native的邏輯代碼并沒有改變历葛。

  • 歸納

上面代碼片段中的getAndRunJSPatchScript函數(shù)只是簡單的調(diào)用了接口正塌,獲取了腳本,然后執(zhí)行恤溶。更長遠(yuǎn)的規(guī)劃,服務(wù)端完全可以實現(xiàn)一個腳本管理服務(wù)帜羊,提供一系列結(jié)構(gòu)咒程,將不同App版本,不同運(yùn)營行為的腳本進(jìn)行分類管理讼育,App只需要上報自己的App基本信息帐姻,然后由服務(wù)端來返回要熱更新的腳本,更加的靈活和體系化奶段。

總結(jié)

上述內(nèi)容可以看到JSPatch確實是一個很方便的熱更新庫饥瓷,但是個人認(rèn)為在App的開發(fā)過程中,大家還是應(yīng)該把質(zhì)量管控在開發(fā)階段痹籍,不可依賴于這樣的熱更新修復(fù)呢铆,這樣的修復(fù)可以用于應(yīng)急來修補(bǔ)漏網(wǎng)的bug, 但是不建議作為App的主要邏輯開發(fā)層,因為這樣的會帶來更多的外部管理App的功能蹲缠,從某種意義上來說也添加了運(yùn)營的復(fù)雜性棺克, 而且在蘋果主推的未來開發(fā)語言swift中,這種OC語言所具有的動態(tài)性將會不再存在线定,以這套原理來運(yùn)作的JSPatch庫也就無從使用了娜谊,但是在當(dāng)下的實際OC仍然是很多App的主開發(fā)語言的時代,這個熱更新修復(fù)的庫還是非常非常實用的斤讥,至于各位開發(fā)者將會對這個庫依賴多深纱皆,這就是自己定奪的問題了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市派草,隨后出現(xiàn)的幾起案子撑帖,更是在濱河造成了極大的恐慌,老刑警劉巖澳眷,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胡嘿,死亡現(xiàn)場離奇詭異,居然都是意外死亡钳踊,警方通過查閱死者的電腦和手機(jī)衷敌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拓瞪,“玉大人缴罗,你說我怎么就攤上這事〖拦。” “怎么了面氓?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蛆橡。 經(jīng)常有香客問我舌界,道長,這世上最難降的妖魔是什么泰演? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任呻拌,我火速辦了婚禮,結(jié)果婚禮上睦焕,老公的妹妹穿的比我還像新娘藐握。我一直安慰自己,他們只是感情好垃喊,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布猾普。 她就那樣靜靜地躺著,像睡著了一般本谜。 火紅的嫁衣襯著肌膚如雪初家。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天耕突,我揣著相機(jī)與錄音笤成,去河邊找鬼。 笑死眷茁,一個胖子當(dāng)著我的面吹牛炕泳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播上祈,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼培遵,長吁一口氣:“原來是場噩夢啊……” “哼浙芙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起籽腕,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤嗡呼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后皇耗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體南窗,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年郎楼,在試婚紗的時候發(fā)現(xiàn)自己被綠了万伤。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡呜袁,死狀恐怖敌买,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情阶界,我是刑警寧澤虹钮,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站膘融,受9級特大地震影響芙粱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜托启,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一宅倒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧屯耸,春花似錦、人聲如沸蹭劈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽铺韧。三九已至多矮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間哈打,已是汗流浹背塔逃。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留料仗,地道東北人湾盗。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像立轧,于是被迫代替她去往敵國和親格粪。 傳聞我的和親對象是個殘疾皇子躏吊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評論 2 355

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