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. 设置函数环境——setfenv

    当我们在全局环境中定义变量时经常会有命名冲突,尤其是在使用一些库的时候,变量声明可能会发生覆盖,这时候就需要一个非全局的环境来解决这问题.setfenv函数可以满足我们的需求. setfenv(f, ...

  2. C++ Code_StatusBar

    主题 1. 创建状态栏 并显示 2. 在状态栏中显示进度条 3. MDI文档显示和隐藏状态栏 4. 5.     代码::创建状态栏 并显示 //手动添加3个ICON //////////////// ...

  3. HDU 1560 DNA sequence DFS

    题意:找到一个最短的串,使得所有给出的串是它的子序列,输出最短的串的长度,然后发现这个串最长是40 分析:从所给串的最长长度开始枚举,然后对于每个长度,暴力深搜,枚举当前位是哪一个字母,注意剪枝 注: ...

  4. Eclipse 经验之谈(一):快速打war包

    如何快速打一个war包: 具体步骤:  单击右键[在项目名称上]——>Export -->War File . 完成war包的导出了.嘻嘻

  5. div高度自适外层div高度随里层div高度自适

    尝试过许多办法 其中一网友的最靠谱就是在外层div样式添加两个标签(不能少) clear:both;  overflow:auto;

  6. PHP单例模式--典型的三私一公

    单例模式:即一个类只被实例化一次,当其他人对其再次实例化时,便返回第一次实例化的对象.这种模式可以极大地节约资源.典型应用于数据库类的实例化. 以实例化一个Mysql数据库类为例: 要实现一个类只实例 ...

  7. arm_linux_device_mem内存映射

    /dev/mem: 物理内存的全镜像.可以用来访问物理内存. /dev/kmem: kernel看到的虚拟内存的全镜像.可以用来访问kernel的内容. /dev/mem 用来访问物理IO设备比如X ...

  8. gitbook 入门教程之常用命令详解

    不论是 gitbook-cli 命令行还是 gitbook editor 编辑器都离不开 gitbook 命令的操作使用,所以再次了解下常用命令. 注意 gitbook-cli 是 gitbook 的 ...

  9. Topshelf:一款非常好用的 Windows 服务开发框架

    背景 多数系统都会涉及到“后台服务”的开发,一般是为了调度一些自动执行的任务或从队列中消费一些消息,开发 windows service 有一点不爽的是:调试麻烦,当然你还需要知道 windows s ...

  10. C++设计模式——状态模式

    前言 在实际开发中,我们经常会遇到这种情况:一个对象有多种状态,在每一个状态下,都会有不同的行为.那么在代码中我们经常是这样实现的. typedef enum tagState { state, st ...