?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上下載源碼
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
- 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)簿训。流程見下圖
JSPatch怎么將js的實(shí)現(xiàn)替換成oc中原有缺陷的實(shí)現(xiàn)
利用runtime - swizzle 簡稱黑盒子方法,將方法實(shí)現(xiàn)指針替換,講runtime的文章很多强品,在這里不細(xì)談膘侮,添下實(shí)現(xiàn),也可下載demo runtime demo
+ (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框架入門介紹