iOS应用动态部署方案
iOS的动态部署能极大的节约成本。苹果的审核周期很长,有的时候,你可能不得不等待将近2个星期去上架你的新功能或者bug。所以动态部署是有价值的。
我这里讨论的情况不把纯web应用考虑在内,因为用户体验过于差,偶尔出现几个页面还可以,但是整个app都是的话,无疑是非常糟糕的。
理论上讲,因为OC是一门动态语言,你可以利用runtime框架,把任意脚本语言,只要该脚本的解释器是由c语言解释器写成,就可以实现由文本向代码的转变。甚至你也可以自己实现一个解释器(也许会有人喜欢造这样的轮子),不过太小众的话,可能除了你自己,就没有人可以维护了。
说说比较大众的解决方案:
第一个是lua
lua目前来讲,更多的是应用在游戏上,通常游戏的包都很大,让玩家常常来更新你们的客户端,那么等着玩家删掉你们的客户端吧。lua第一次名声大噪大概是被魔兽世界所应用,现在手游上cocos-2d中的lua版本即便算不上很主流,但使用的厂商仍然不少。
iOS中,由阿里维护的wax是个比较好的选择,使用起来也比较稳定,具体的仍可以参见github上的文档,感觉阿里的同学的维护。
https://github.com/alibaba/wax
lua的优点在于:解释的速度要比js(下面介绍)要快一些
lua的缺点在于:需要将整个lua的解释器打入程序中,不过这也是可以接受的。另外lua的开发者可能会少一些,在招人上可能难一些。
第二个是js
js目前也是更多的应用在游戏上,由于游戏的特性驱动所产生技术的产生与成熟,确实让游戏在动态部署成熟了些。cocos-2d的js版本应用要比lua版本广泛一些。腾讯的bang同学为我们开源了 JSPatch,向他致敬。
https://github.com/bang590/JSPatch
js的优点在于:系统内置了js的解释器(iOS7及之后),会js的人多。
js的缺点在于:解释稍慢。
以上两种动态部署方案,我个人都尝试过,出现的一些坑,肯定要写出来分享给大家。
1.库冲突问题。这也是我为什么两种方案都尝试的原因。我在首先尝试的JSPatch,发现Aspects这个框架
https://github.com/steipete/Aspects 做一些AOP变成的hook库。
同时使用了_objc_msgForward这个IMP,上面是Aspects,下面是JSPatch。我抱着侥幸的心里,虽然已经认识到WaxPatch极有可能也是如此实现的,去尝试了WaxPatch,果不其然,依旧不行,看最下面的代码,就是lua的。
static BOOL aspect_isMsgForwardIMP(IMP impl) {
return impl == _objc_msgForward
#if !defined(__arm64__)
|| impl == (IMP)_objc_msgForward_stret
#endif
;
}
static void overrideMethod(Class cls, NSString *selectorName, JSValue *function, BOOL isClassMethod, const char *typeDescription)
{
SEL selector = NSSelectorFromString(selectorName);
if (!typeDescription) {
Method method = class_getInstanceMethod(cls, selector);
typeDescription = (char *)method_getTypeEncoding(method);
}
IMP originalImp = class_respondsToSelector(cls, selector) ? class_getMethodImplementation(cls, selector) : NULL;
IMP msgForwardIMP = _objc_msgForward;
#if !defined(__arm64__)
if (typeDescription[0] == '{') {
//In some cases that returns struct, we should use the '_stret' API:
//http://sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html
//NSMethodSignature knows the detail but has no API to return, we can only get the info from debugDescription.
NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:typeDescription];
if ([methodSignature.debugDescription rangeOfString:@"is special struct return? YES"].location != NSNotFound) {
msgForwardIMP = (IMP)_objc_msgForward_stret;
}
}
#endif
class_replaceMethod(cls, selector, msgForwardIMP, typeDescription);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
if (class_getMethodImplementation(cls, @selector(forwardInvocation:)) != (IMP)JPForwardInvocation) {
IMP originalForwardImp = class_replaceMethod(cls, @selector(forwardInvocation:), (IMP)JPForwardInvocation, "v@:@");
class_addMethod(cls, @selector(ORIGforwardInvocation:), originalForwardImp, "v@:@");
}
#pragma clang diagnostic pop
if (class_respondsToSelector(cls, selector)) {
NSString *originalSelectorName = [NSString stringWithFormat:@"ORIG%@", selectorName];
SEL originalSelector = NSSelectorFromString(originalSelectorName);
if(!class_respondsToSelector(cls, originalSelector)) {
class_addMethod(cls, originalSelector, originalImp, typeDescription);
}
}
NSString *JPSelectorName = [NSString stringWithFormat:@"_JP%@", selectorName];
SEL JPSelector = NSSelectorFromString(JPSelectorName);
_initJPOverideMethods(cls);
_JSOverideMethods[cls][JPSelectorName] = function;
class_addMethod(cls, JPSelector, msgForwardIMP, typeDescription);
}
static BOOL isMethodReplacedByInvocation(id klass, SEL selector){
Method selectorMethod = class_getInstanceMethod(klass, selector);
IMP imp = method_getImplementation(selectorMethod);
#if defined(__arm64__)
return imp == _objc_msgForward;
#else
return imp == _objc_msgForward || imp == (IMP)_objc_msgForward_stret;
#endif
}
static BOOL isMethodReplacedByInvocation(id klass, SEL selector){
Method selectorMethod = class_getInstanceMethod(klass, selector);
IMP imp = method_getImplementation(selectorMethod);
#if defined(__arm64__)
return imp == _objc_msgForward;
#else
return imp == _objc_msgForward || imp == (IMP)_objc_msgForward_stret;
#endif
}
static void replaceMethodAndGenerateORIG(id klass, SEL selector, IMP newIMP){
Method selectorMethod = class_getInstanceMethod(klass, selector);
const char *typeDescription = method_getTypeEncoding(selectorMethod);
IMP prevImp = class_replaceMethod(klass, selector, newIMP, typeDescription);
if(prevImp == newIMP){
// NSLog(@"Repetition replace but, never mind");
return ;
}
const char *selectorName = sel_getName(selector);
char newSelectorName[strlen(selectorName) + 10];
strcpy(newSelectorName, WAX_ORIGINAL_METHOD_PREFIX);
strcat(newSelectorName, selectorName);
SEL newSelector = sel_getUid(newSelectorName);
if(!class_respondsToSelector(klass, newSelector)) {
BOOL res = class_addMethod(klass, newSelector, prevImp, typeDescription);
// NSLog(@"res=%d", res);
}
}
既然冲突了,就必须解决冲突。鱼与熊掌,二者不可得兼,舍鱼而取熊掌。只能把 Aspects干掉了。
可是Aspects,实现的方法如何去替换呢?我就直接用了method swizzle。简单的实现了下Aspects的功能,但是没那么优雅。
@implementation UIViewController (AOP)
+ (void)initialize {
Method ori_Method = class_getInstanceMethod([UIViewController class], @selector(viewDidAppear:));
Method my_Method = class_getInstanceMethod([UIViewController class], @selector(aop_viewDidAppear:));
method_exchangeImplementations(ori_Method, my_Method);
}
- (void)aop_viewDidAppear:(BOOL)animated {
[self aop_viewDidAppear:animated];
NSLog(@"------hook");
}
@end
对,就是简单的hook一下。不过你要考虑清楚,对同一个方法hook几次,最后的执行顺序问题。
ReactiveCocoa这个框架也可能存在类似问题,可能没那么好解决了。这个时候可能要做一些取舍,如果你是leader的话,可能要制定下规范来避免这个问题,
什么方法不可以用,要如何hook等等,暂时我也没有太好的解决方案。
iOS应用动态部署方案的更多相关文章
- iOS动态部署方案
转载: iOS动态部署方案 前言 这里讨论的动态部署方案,就是指通过不发版的方式,将新的内容.新的业务流程部署进已发布的App.因为苹果的审核周期比较长,而且苹果的限制比较多,业界在这里也没有特别多的 ...
- Jenkins动态部署方案
在之前一个项目开发中使用到了jenkins自动化测试,根据实际应用,简单整理了其部署方案. 1.部署 2.项目构建 3.重部署 1 部署 登录Jenkins应用管理界面 1)选中一个服务器上已在jen ...
- iOS动态部署之RSA加密传输Patch补丁
概要:这一篇博客主要说明下iOS客户端动态部署方案中,patch(补丁)是如何比较安全的加载到客户端中. 在整个过程中,需要使用RSA来加密(你可以选择其它的非对称加密算法),MD5来做校验(同样,你 ...
- iOS应用架构谈 本地持久化方案及动态部署
转载: iOS应用架构谈 本地持久化方案及动态部署 前言 嗯,你们要的大招.跟着这篇文章一起也发布了CTPersistance和CTJSBridge这两个库,希望大家在实际使用的时候如果遇到问题,就给 ...
- iOS应用架构谈part4-本地持久化方案及动态部署
前言 嗯,你们要的大招.跟着这篇文章一起也发布了CTPersistance和CTJSBridge这两个库,希望大家在实际使用的时候如果遇到问题,就给我提issue或者PR或者评论区.每一个issue和 ...
- ssiOS应用架构谈 本地持久化方案及动态部署
本文转载至 http://casatwy.com/iosying-yong-jia-gou-tan-ben-di-chi-jiu-hua-fang-an-ji-dong-tai-bu-shu.html ...
- fir.im Weekly - iOS / Android 动态化更新方案盘点
动态化更新是 App 开发必然面对的问题.在 iOS 环境下,Apple 开发者们像是" 带着手铐脚镣跳舞" ,相比之下 Android 开发者会轻松一点,有很多相关的开源框架帮助 ...
- iOS 中的 HotFix 方案总结详解
相信HotFix大家应该都很熟悉了,今天主要对于最近调研的一些方案做一些总结.iOS中的HotFix方案大致可以分为四种: WaxPatch(Alibaba) Dynamic Framework(Ap ...
- ActiveMQ实现负载均衡+高可用部署方案
一.架构和技术介绍 1.简介 ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线.完全支持JMS1.1和J2EE 1.4规范的JMS Provider实现 2.activemq的特 ...
随机推荐
- 基于网格的分割线优化算法(Level Set)
本文介绍一种网格分割线的优化算法,该方法能够找到网格上更精确.更光滑的分割位置,并且分割线能够自由地合并和分裂,下面介绍算法的具体原理和过程. 曲面上的曲线可以由水平集(level set)形式表示, ...
- BZOJ 3343: 教主的魔法 [分块]【学习笔记】
3343: 教主的魔法 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1172 Solved: 526[Submit][Status][Discus ...
- cin
cin 是预定义的标准输入流对象,cin 用来接收字符串时,遇“空格”.“TAP”.“回车”时都会结束.
- JSP前三章测试改错
分析:文件.数据库都是持久化保存数据的,所以是正确的,而servletcontext是上下文对象当然也可以.所以正确答案为A 分析: A:判断学员是否手动安装过Tomcat(练习熟练度) B:使学员了 ...
- java中两个字符串如何比较大小
有三种方法实现 第一种直接用字符串类的compareTo方法: 1 2 3 String t1="20131011"; String t2="20131030&q ...
- 嵌入式Linux驱动学习之路(二十一)字符设备驱动程序总结和块设备驱动程序的引入
字符设备驱动程序 应用程序是调用C库中的open read write等函数.而为了操作硬件,所以引入了驱动模块. 构建一个简单的驱动,有一下步骤. 1. 创建file_operations 2. 申 ...
- Membership 重置密码
public ActionResult ChongZhiMiMa() { Membership.GetUser("admin").UnlockU ...
- gradle项目中如何支持java与scala混合使用?
之前写过一篇maven项目中java与scala如何混用,今天来看看gradle项目中如何达到同样的效果: 一.目录结构 基本上跟maven一样,tips:这一堆目录结构不用死记,后面会讲如何用gra ...
- 物联网(莹石云)WIFI一键配置原理分析(zz)
最近打算做一款自己的无线传输模块用来实现光伏电站的数据接入,希望可以尽量简化接入流程,其中wifi密码的配置就是一个比较麻烦的事情,想到最近使用萤石摄像头时,wifi密码配置似乎很简单,他们是怎么做到 ...
- 2.0 (1)安装MongoDB
(官网:www.mongodb.com) ——————————(1)Mac安装MongoDB———————— 1)安装homebrew (官网地址,brew.sh) ruby -e "$(c ...