iOSJSPatch热更新(HotFix)线上bug

iOSJSPatch热更新(HotFix)线上bug

2023年7月14日发(作者:)

iOSJSPatch热更新(HotFix)线上bug苹果⼤⼤今天发出了警告邮件,貌似禁掉了热更新;具体关注 JSPatch issues 和Facebook RN框架回复或是我的这个⽂章:苹果爸爸放⼤招,禁掉JSPatch热更新(Apple爹等已给回复)⾸先以我的理解介绍下JSPatch的应⽤场景、⼯作原理和使⽤⽅法:1、应⽤场景:当我们已经发布上线了项⽬,在使⽤过程中发现了bug,紧急发包来不及,或是不值得发⼀次包的时候,可以使⽤JSPatch来热修复这个bug。2、⼯作原理:⾸先将项⽬中的bug修复好,再将这个⽅法(也有可能是⼀个类、甚⾄新建⼀个类)转换成对应的JS代码格式【ps:为什么使⽤js代码呢,因为服务器PHP不能识别我们的oc代码】,交给后台上传到服务器。等到下次启动APP的时候,我们就要获取到这个JS内容(你可以选择下载下来整个JS⽂件,或是只取得我们想要的JS内容),当我们在运⾏到之前那个bug的时候,就会将这个⽅法拦截,执⾏JS新的⽅法,即替换掉了有bug的这个⽅法,从⽽完成了热修复。3、使⽤⽅法:第⼀次部署的时候,和后台服务器约定好,怎么获取到这个JS⽂件,⼀般新开⼀个接⼝,专门获取这个JS⽂件以及判断时候需要更新(设置⼀个值,有变化时就更新)。为了防⽌别⼈轻易截取我们的⽅法,我们要对JS⽂件加密处理。安全性加密.png4、其他:我们可以使⽤JSPatch热修复bug,甚⾄可以开发新的项⽬。JSPatch也可以多个版本留存兼容。注意:热修复我认为是⾮常规⼿法,所以我们的bug在下⼀个版本发布前必须⽤OC代码修复,不可在⾛热修复。 具体请看JSPatch作者⽂档。下⾯把相关链接贴出来:在此感谢作者开源 github:JSPatch、 JSPatch-Wiki 、和在线转换⼯具 以及OC代码转JS⽂件的基本规则:基础⽤法现在已经有了官⽅⽹站,直接集成sdk,更⽅便了。JSPatch 官⽹常见的转换注意事项及转换格式:**基础原理**JSPatch 能做到通过 JS 调⽤和改写 OC ⽅法最根本的原因是 Objective-C 是动态语⾔,OC 上所有⽅法的调⽤/类的⽣成都通过 Objective-C Runtime 在运⾏时进⾏,我们可以通过类名/⽅法名反射得到相应的类和⽅法:Class class = NSClassFromString("UIViewController");id viewController = [[class alloc] init];SEL selector = NSSelectorFromString("viewDidLoad");[viewController performSelector:selector];// 也可以替换某个类的⽅法为新的实现static void newViewDidLoad(id slf, SEL sel) {}class_replaceMethod(class, selector, newViewDidLoad, @"");还可以新注册⼀个类,为类添加⽅法:Class cls = objc_allocateClassPair(superCls, "JPObject", 0);objc_registerClassPair(cls);class_addMethod(cls, selector, implement, typedesc);注意点:1,关于打印:不能使⽤NSLog函数。("success");var tmpAAAA = ctUrlStr();(tmpAAAA);2,isKindOfClass,if (OfClass(())) {}3,OC的常量,需要⽤他具体的值来代替。TURE/YES(1),FALSE/NO(0)也是。4、引⽤的宏要写对应具体的数值代替。eg:UIControlStateNormal ----> 0, UIControlStateHighlighted ----> 1 << 0,

5,空串判断if (_redirectUrlStr) // ⽆法识别,需要⽤⼀下⽅法来判断if (ctUrlStr().length() > 0)

6,需要修改多个⽅法:[多个⽅法⽤法](/p/a8e1f3f07f5c)defineClass('SampleClassA', {});defineClass('SampleClassB', {});7,CGRect取值:rect.x,rect.y,, --》 [].width,[].top,[].leftUIEdg:({top:0, left:().width*0.5, bottom:0, right:0})({x:0,y:0,width: reen().bounds().width, height:reen().bounds().height})8,拼接字符串:替换stringformatqer⽅法。var text = ndiseDiscount().toFixed(1)+'折';var marketPrice = '¥'+ Price();(有时候,直接这样拼接,会显⽰Object[Object]),这貌似根JS的字符段对象有关系。ps:toFixed(n) ---> 保留⼏位⼩数9,获取数据个数:ags().count()10,获取数组对应下标元素:AtIndex(0)获取字典的值:要⽤objectForKey; eg:var cellType = ForKey("cellType");11,for in循环,改成for循环。for (var i = 0; i < (); i++){ var lbl = AtIndex(i); den(1);}12,__weak 和 __strongvar weakSelf = __weak(self)block⾥的self 要转换成这样:var tmpSelf = self;13,代理⽅法if (legate() && legate().respondsToSelector("webViewDidFailLoadWithError:"))

{ legate().webViewDidFailLoadWithError(error);}14,原OC⽅法⾥的,带下划线的属性,或者是调⽤的带下划线的⽅法名,需要⽤两个下划线代替。14,原OC⽅法⾥的,带下划线的属性,或者是调⽤的带下划线的⽅法名,需要⽤两个下划线代替。_maidian() —> __maidian()sd_setImageWithURL —> sd__setImageWithURL15,带下划线的私有变量。获取:var tmpProduct = orKey("_product");获取:var productName = orKey("_product_name");(获取时,直接将下划线删掉)赋值:ue_forKey(product, "_product");16,⽅法⾥⽤的所有私有变量,都要先获取,才可以使⽤,直接⽤私有变量是错误的。var maiDianLabel = orKey("_maiDianLabel") ;(获取买点的Label私有变量)17,拦截的⽅法⾥,只要出现类名的,都需要放到require⾥,逗号隔开。系统⾃动可能转义不全,⾃⼰添加。require('NSString,UIImage,NSURL,UIColor,WZHProduct, MASConstraintMaker,UIScreen');18,⼀些固定的变量,例如字体样式的key,先通过代码,NSLog打印出具体的值,然后再替换JS代码。OC:@{NSFontAttributeName :_}JS:{NSFont: ()}19,拦截系统的代理⽅法的时候。必须要测试。像WebView的封装类,拦截了WebView的代理⽅法(估计是UIWebView也是⽤了OC的JS库,冲突导致)20,block**限制**从 JS 传 block 到 OC,有两个限制:A. block 参数个数最多⽀持6个。(若需要⽀持更多,可以修改源码)B. block 参数类型不能是 double / NSBlock / struct 类型。附上⼀段转义的代码OC代码:类名:WebActivityVC.h-(void)managerPushViewController:(UIViewController *)viewController{ if ([viewController isKindOfClass:[LoginVC class]]) { BaseNavigationController *navigate = [[BaseNavigationController alloc] initWithRootViewController:viewController]; [tionController presentViewController:navigate animated:YES completion:nil]; return; } [tionController pushViewController:viewController animated:YES];}```对应的JS代码:require('LoginVC,BaseNavigationController');defineClass('WebActivityVC', {managerPushViewController: function(viewController) {if (OfClass(())) {var navigate = ().initWithRootViewController(viewController);tionController().presentViewController_animated_completion(navigate, YES, null);return;}tionController().pushViewController_animated(viewController, YES);},});```客户端具体策略:1.⽤户打开App时,同步进⾏本地补丁的加载。2.⽤户打开App时,后台进程发起异步⽹络请求,获取服务器中当前App版本所对应的最新补丁版本和必须的补丁版本。3.获取补丁版本的请求回来后,跟本地的补丁版本进⾏对⽐。4.如果本地补丁版本⼩于必须版本,则提⽰⽤户,展⽰下载补丁界⾯,进⾏进程同步的补丁下载。下载完成后重新加载App和最新补丁,再进⼊App。5.如果本地补丁版本不⼩于必须版本,但⼩于最新版本,则进⼊App,不影响⽤户操作。同时进⾏后台进程异步静默下载,下载后补丁保存在本地。下次App启动时再加载最新补丁。6.如果版本为最新,则进⼊App。流程图.png下⾯来说下具体怎么集成到项⽬中1、你可以先搞⼀个本地测试⽂件,⽅便需要使⽤的时候调试。直接本地存储就好了。2、⼤体步骤就是:先和服务器约定好了,让我可以取到这个需要的JS⽂件。3、APP中部署:经验建议1、重要的地⽅,⽐如购物流程、涉及到⽀付、⽣成订单、选择⽀付⽅式等事件动作的地⽅进⾏JSPatch埋点。以防⼀旦出现bug,只需要修改这个埋点事件,不⽤整个⽅法都修改。2、尽量能简单调⽤⽅法,就不要把很多处理放在同⼀个⽅法中。防⽌⼀旦需要热修复,要修改很多不必要的逻辑⽅法。费时间。。3、声明属性、变量的时候,不要写下划线。这是致命的。。⼀般⼈也不会这么写吧:@property (nonatomic, strong) UIButton *_loginOutBtn;给我把这个_loginOutBtn前⾯下划线去掉。去掉。。⼩技巧(很有⽤哦)如果你的⽅法代码⾮常多,这个时候整个⽅法代码进⾏代码转换就相当耗时间。如果你要修改的代码刚好在⽅法的最后⾯(哪个位置都适⽤啦),这个时候我们可以把⽅法前⾯的代码⽤下⾯代码替代//在我们需要替换的⽅法名前⾯加上ORIGupdateContent⽅法dateContent();//在这⾏代码后⾯加上你需要修改的代码,上⾯⼀句代码相当于把整个⽅法已经执⾏了⼀遍,所以我们不需要把整个⽅法的代码进⾏替换```例如:下⾯这个⽅法本来是有200⾏代码,但是我刚好要替换的代码是在最后⾯,所以我在⽅法前⾯加上dateContent();就相当于是把⽅法已经执⾏了⼀遍,就不需要![例⼦.png](/upload_images/?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)PS:①.接⼊了JSPatch之后,iOS的线上BUG 看上去就不向以前那样“猛如虎”了,但是这仅仅是⼀个紧急预案措施,以前规范的流程还是需要遵守。②.每⼀次本版本⽤JSPatch解决的线上Bug,下个版本必须⽤OC代码写⼊项⽬中,不能允许补丁代码的存留超过⼀个版本。④.每次⽤JSPatch解决掉的线上BUG 应当有⼀个专门的⽂档记录,遇到重复错误必须写casestudy。③.倡导使⽤敏捷开发的思想,类似于主逻辑或者是功能模块⼊⼝的⽅法可以抽的更细,这样即使需要修改,成本也不会太⼤,作者本⼈也提到,如果有⼀⾏代码必须要在⼀个⼤⽅####原谅我在最下⾯告诉你们最新的⽅法 毕竟我写了这么多不容易啊

JSPatch 有现成的SDK,直接拖SDK到⼯程中就可,借助它的平台,进⾏补丁的上传⼯作,省去了⾃⼰配置服务器等。**但是 但是** 这么好的东西,作者也不容易啊,所以这种⽅

发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1689265198a226456.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信