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应用动态部署方案的更多相关文章

  1. iOS动态部署方案

    转载: iOS动态部署方案 前言 这里讨论的动态部署方案,就是指通过不发版的方式,将新的内容.新的业务流程部署进已发布的App.因为苹果的审核周期比较长,而且苹果的限制比较多,业界在这里也没有特别多的 ...

  2. Jenkins动态部署方案

    在之前一个项目开发中使用到了jenkins自动化测试,根据实际应用,简单整理了其部署方案. 1.部署 2.项目构建 3.重部署 1 部署 登录Jenkins应用管理界面 1)选中一个服务器上已在jen ...

  3. iOS动态部署之RSA加密传输Patch补丁

    概要:这一篇博客主要说明下iOS客户端动态部署方案中,patch(补丁)是如何比较安全的加载到客户端中. 在整个过程中,需要使用RSA来加密(你可以选择其它的非对称加密算法),MD5来做校验(同样,你 ...

  4. iOS应用架构谈 本地持久化方案及动态部署

    转载: iOS应用架构谈 本地持久化方案及动态部署 前言 嗯,你们要的大招.跟着这篇文章一起也发布了CTPersistance和CTJSBridge这两个库,希望大家在实际使用的时候如果遇到问题,就给 ...

  5. iOS应用架构谈part4-本地持久化方案及动态部署

    前言 嗯,你们要的大招.跟着这篇文章一起也发布了CTPersistance和CTJSBridge这两个库,希望大家在实际使用的时候如果遇到问题,就给我提issue或者PR或者评论区.每一个issue和 ...

  6. ssiOS应用架构谈 本地持久化方案及动态部署

    本文转载至 http://casatwy.com/iosying-yong-jia-gou-tan-ben-di-chi-jiu-hua-fang-an-ji-dong-tai-bu-shu.html ...

  7. fir.im Weekly - iOS / Android 动态化更新方案盘点

    动态化更新是 App 开发必然面对的问题.在 iOS 环境下,Apple 开发者们像是" 带着手铐脚镣跳舞" ,相比之下 Android 开发者会轻松一点,有很多相关的开源框架帮助 ...

  8. iOS 中的 HotFix 方案总结详解

    相信HotFix大家应该都很熟悉了,今天主要对于最近调研的一些方案做一些总结.iOS中的HotFix方案大致可以分为四种: WaxPatch(Alibaba) Dynamic Framework(Ap ...

  9. ActiveMQ实现负载均衡+高可用部署方案

    一.架构和技术介绍 1.简介 ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线.完全支持JMS1.1和J2EE 1.4规范的JMS Provider实现 2.activemq的特 ...

随机推荐

  1. Windows7台式电脑怎么调节屏幕亮度

     

  2. Microsoft-Office-Professional-Plus-2007

    Microsoft-Office-Professional-Plus-2007password:(也有自带的)DP37G-8BBDM-9Y4BW-WT2K8-2WRMJ P64QH-V3F2K-RXY ...

  3. 在github上建立自己的网站

    学了前端小半年,如今写了个自己的网页想要去应聘,却发现部署很麻烦,部署到阿里云之类,买域名啊啥的还要收费,说贵也不贵,但我就是傲娇~ google一下了解到Github有一个Github pages的 ...

  4. NOIP模拟赛20161114

    幸运串 题意:长度为n,字符集大小为m的字符串中有多少不同的不含回文的串 n,m<10^9 我靠这不就是萌数的DP部分吗 有规律 f[2][j][k]=1 f[i][j][k]=sigma{f[ ...

  5. JS文件加载:比较async和DOM Script

    async与script动态加载都能使文件异步加载,本文叙述它们对页面渲染和load加载的影响方面. 目前我用demo.js作为执行文件操作.代码: var now = function() { re ...

  6. PhpStorm XDebug 远程调试

    现在我们自己公司的各种开发和测试服务器,都是使用阿里云的服务器.一些PHP的项目,无法在本地搭建完整的环境,在外网服务器上调试更方便定位问题.发现网上没有完整的关于如何配置PHPStorm和XDebu ...

  7. 潭州学院-JavaVIP的Javascript的高级进阶-KeKe老师

    潭州学院-JavaVIP的Javascript的高级进阶-KeKe老师 讲的不错,可以学习 下面是教程的目录截图: 下载地址:http://www.fu83.cn/thread-283-1-1.htm ...

  8. C#如何防止程序多次运行的技巧

    一.使用互斥量Mutex弄懂了主要的实现思路之后,下面看代码实现就完全不是问题了,使用互斥量的实现就是第四点的思路的体现,我们用为该程序进程创建一个互斥量Mutex对象变量,当运行该程序时,该程序进程 ...

  9. VUE --- 给页面加点网络动态数据

    这时候的页面都是静态的(数据在写程序的时候已经固定了不能修改),而每个应用基本上都会请求外部数据以动态改变页面内容.对应有一个库叫 vue-resource 帮我们解决这个问题. 使用命令行安装 cn ...

  10. python文件读写操作

    操作文件时一般经历以下步骤: 打开文件 操作文件 1.打开文件: 文件句柄 = open('/path/to/file_name','pattern') pattern: r:只读模式,打开文件时默认 ...