JSPatch – 動(dòng)態(tài)更新iOS APP

JSPatch是最近業(yè)余做的項(xiàng)目榆骚,只需在項(xiàng)目中引入極小的引擎,就可以使用JavaScript調(diào)用任何Objective-C的原生接口,獲得腳本語(yǔ)言的能力:動(dòng)態(tài)更新APP盟蚣,替換項(xiàng)目原生代碼修復(fù)bug。

用途

是否有過這樣的經(jīng)歷:新版本上線后發(fā)現(xiàn)有個(gè)嚴(yán)重的bug卖怜,可能會(huì)導(dǎo)致crash率激增屎开,可能會(huì)使網(wǎng)絡(luò)請(qǐng)求無(wú)法發(fā)出,這時(shí)能做的只是趕緊修復(fù)bug然后提交等待漫長(zhǎng)的AppStore審核马靠,再盼望用戶快點(diǎn)升級(jí)奄抽,付出巨大的人力和時(shí)間成本,才能完成此次bug的修復(fù)甩鳄。

使用JSPatch可以解決這樣的問題逞度,只需在項(xiàng)目中引入JSPatch,就可以在發(fā)現(xiàn)bug時(shí)下發(fā)JS腳本補(bǔ)丁娩贷,替換原生方法第晰,無(wú)需更新APP即時(shí)修復(fù)bug。

例子

@implementation JPTableViewController
...

  • (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
    NSString *content = self.dataSource[[indexPath row]]; //可能會(huì)超出數(shù)組范圍導(dǎo)致crash
    JPViewController *ctrl = [[JPViewController alloc] initWithContent:content];
    [self.navigationController pushViewController:ctrl];
    }
    ...
    @end
    上述代碼中取數(shù)組元素處可能會(huì)超出數(shù)組范圍導(dǎo)致crash彬祖。如果在項(xiàng)目里引用了JSPatch茁瘦,就可以下發(fā)JS腳本修復(fù)這個(gè)bug:

import “JPEngine.m"

@implementation AppDelegate

  • (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
    [JPEngine startEngine];
    [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://cnbang.net/bugfix.JS"]] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
    NSString *script = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    if (script) {
    [JPEngine evaluateScript:script];
    }
    }];
    ….
    return YES;
    }
    @end
    //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);
    }
    }
    }, {})
    這樣 JPTableViewController 里的 -tableView:didSelectRowAtIndexPath: 就替換成了這個(gè)JS腳本里的實(shí)現(xiàn),在用戶無(wú)感知的情況下修復(fù)了這個(gè)bug储笑。

更多的使用文檔和demo請(qǐng)參考github項(xiàng)目主頁(yè)甜熔。

原理

JSPatch用iOS內(nèi)置的JavaScriptCore.framework作為JS引擎,但沒有用它JSExport的特性進(jìn)行JS-OC函數(shù)互調(diào)突倍,而是通過Objective-C Runtime腔稀,從JS傳遞要調(diào)用的類名函數(shù)名到Objective-C盆昙,再使用NSInvocation動(dòng)態(tài)調(diào)用對(duì)應(yīng)的OC方法。詳細(xì)的實(shí)現(xiàn)原理以及實(shí)現(xiàn)過程中遇到的各種坑和hack方法會(huì)另有文章介紹焊虏。

方案對(duì)比

目前已經(jīng)有一些方案可以實(shí)現(xiàn)動(dòng)態(tài)打補(bǔ)丁淡喜,例如WaxPatch,可以用Lua調(diào)用OC方法诵闭,相對(duì)于WaxPatch炼团,JSPatch的優(yōu)勢(shì)是:

1.JS語(yǔ)言

JS比Lua在應(yīng)用開發(fā)領(lǐng)域有更廣泛的應(yīng)用,目前前端開發(fā)和終端開發(fā)有融合的趨勢(shì)疏尿,作為擴(kuò)展的腳本語(yǔ)言瘟芝,JS是不二之選。

2.符合Apple規(guī)則

JSPatch更符合Apple的規(guī)則褥琐。iOS Developer Program License Agreement里3.3.2提到不可動(dòng)態(tài)下發(fā)可執(zhí)行代碼锌俱,但通過蘋果JavaScriptCore.framework或WebKit執(zhí)行的代碼除外,JS正是通過JavaScriptCore.framework執(zhí)行的敌呈。

3.小巧

使用系統(tǒng)內(nèi)置的JavaScriptCore.framework贸宏,無(wú)需內(nèi)嵌腳本引擎,體積小巧驱富。

4.支持block

wax在幾年前就停止了開發(fā)和維護(hù)锚赤,不支持Objective-C里block跟Lua程序的互傳,雖然一些第三方已經(jīng)實(shí)現(xiàn)block褐鸥,但使用時(shí)參數(shù)上也有比較多的限制线脚。

相對(duì)于WaxPatch,JSPatch劣勢(shì)在于不支持iOS6叫榕,因?yàn)樾枰隞avaScriptCore.framework浑侥。另外目前內(nèi)存的使用上會(huì)高于wax,持續(xù)改進(jìn)中晰绎。

風(fēng)險(xiǎn)

JSPatch讓腳本語(yǔ)言獲得調(diào)用所有原生OC方法的能力寓落,不像web前端把能力局限在瀏覽器,使用上會(huì)有一些安全風(fēng)險(xiǎn):

1.若在網(wǎng)絡(luò)傳輸過程中下發(fā)明文JS荞下,可能會(huì)被中間人篡改JS腳本伶选,執(zhí)行任意方法,盜取APP里的相關(guān)信息尖昏⊙鏊埃可以對(duì)傳輸過程進(jìn)行加密,或用直接使用https解決抽诉。

2.若下載完后的JS保存在本地沒有加密陨簇,在未越獄的機(jī)器上用戶也可以手動(dòng)替換或篡改腳本。這點(diǎn)危害沒有第一點(diǎn)大迹淌,因?yàn)椴僮髡呤鞘謾C(jī)擁有者河绽,不存在APP內(nèi)相關(guān)信息被盜用的風(fēng)險(xiǎn)己单。若要避免用戶修改代碼影響APP運(yùn)行,可以選擇簡(jiǎn)單的加密存儲(chǔ)耙饰。

其他用途

JSPatch可以動(dòng)態(tài)打補(bǔ)丁纹笼,自由修改APP里的代碼,理論上還可以完全用JSPatch實(shí)現(xiàn)一個(gè)業(yè)務(wù)模塊苟跪,甚至整個(gè)APP允乐,跟wax一樣,但不推薦這么做削咆,因?yàn)椋?/p>

JSPatch和wax一樣都是通過Objective-C Runtime的接口通過字符串反射找到對(duì)應(yīng)的類和方法進(jìn)行調(diào)用,這中間的字符串處理會(huì)損耗一定的性能蠢笋,另外兩種語(yǔ)言間的類型轉(zhuǎn)換也有性能損耗拨齐,若用來(lái)做一個(gè)完整的業(yè)務(wù)模塊,大量的頻繁來(lái)回互調(diào)昨寞,可能有性能問題瞻惋。
開發(fā)過程中需要用OC的思維寫JS/Lua,喪失了腳本語(yǔ)言自己的特性援岩。
JSPatch和wax都沒有IDE支持歼狼,開發(fā)效率低。
若想動(dòng)態(tài)為APP添加模塊享怀,目前React Native給出了很好的方案羽峰,解決了上述三個(gè)問題:

JS/OC不會(huì)頻繁通信,會(huì)在事件觸發(fā)時(shí)批量傳遞添瓷,提高效率梅屉。(詳見React Native通信機(jī)制詳解)
開發(fā)過程無(wú)需考慮OC的感受,遵從React框架的思想進(jìn)行純JS開發(fā)就行鳞贷,剩下的事情React Native幫你處理好了坯汤。
React Native連IDE都準(zhǔn)備好了。
所以動(dòng)態(tài)添加業(yè)務(wù)模塊目前還是推薦嘗試React Native搀愧,但React Native并不會(huì)提供原生OC接口的反射調(diào)用和方法替換惰聂,無(wú)法做到修改原生代碼,JSPatch以小巧的引擎補(bǔ)足這個(gè)缺口咱筛,配合React Native用統(tǒng)一的JS語(yǔ)言讓一個(gè)原生APP時(shí)刻處于可擴(kuò)展可修改的狀態(tài)搓幌。

目前JSPatch處于開發(fā)階段,穩(wěn)定性和功能還存在一些問題眷蚓,歡迎大家提建議/bug/PR鼻种,一起來(lái)做這個(gè)項(xiàng)目。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末沙热,一起剝皮案震驚了整個(gè)濱河市叉钥,隨后出現(xiàn)的幾起案子罢缸,更是在濱河造成了極大的恐慌,老刑警劉巖投队,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件枫疆,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡敷鸦,警方通過查閱死者的電腦和手機(jī)息楔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)扒披,“玉大人值依,你說我怎么就攤上這事〉福” “怎么了愿险?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)价说。 經(jīng)常有香客問我辆亏,道長(zhǎng),這世上最難降的妖魔是什么鳖目? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任扮叨,我火速辦了婚禮,結(jié)果婚禮上领迈,老公的妹妹穿的比我還像新娘彻磁。我一直安慰自己,他們只是感情好狸捅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布兵迅。 她就那樣靜靜地躺著,像睡著了一般薪贫。 火紅的嫁衣襯著肌膚如雪恍箭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天瞧省,我揣著相機(jī)與錄音扯夭,去河邊找鬼。 笑死鞍匾,一個(gè)胖子當(dāng)著我的面吹牛交洗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播橡淑,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼构拳,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起置森,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤斗埂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后凫海,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體呛凶,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年行贪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了漾稀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡建瘫,死狀恐怖崭捍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情啰脚,我是刑警寧澤缕贡,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站拣播,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏收擦。R本人自食惡果不足惜贮配,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望塞赂。 院中可真熱鬧泪勒,春花似錦、人聲如沸宴猾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)仇哆。三九已至沦辙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間讹剔,已是汗流浹背油讯。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留延欠,地道東北人陌兑。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像由捎,于是被迫代替她去往敵國(guó)和親兔综。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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