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的特 ...
随机推荐
- Web.xml中设置Servlet和Filter时的url-pattern匹配规则
一.servlet容器对url的匹配过程: 当一个请求发送到servlet容器的时候,容器先会将请求的url减去当前应用上下文的路径作为servlet的映射url,比如我访问的是http://loca ...
- Java 线程同步
线程同步 1.线程同步的目的是为了保护多个线程访问一个资源时对资源的破坏. 2.线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的 ...
- C语言基础(一)
7744问题(输出所有形如aabb的4位完全平方数) 方法1: #include<stdio.h> #include<math.h> int main (){ ;a<=; ...
- 【六年开源路】FineUI家族今日全部更新!
FineUI(开源版) 基于 ExtJS 的开源 ASP.NET 控件库 FineUI的使命 创建 No JavaScript,No CSS,No UpdatePanel,No ViewState ...
- [转]Python yield 使用浅析
您可能听说过,带有 yield 的函数在 Python 中被称之为 generator(生成器),何谓 generator ? 我们先抛开 generator,以一个常见的编程题目来展示 yield ...
- 通过OnResultExecuted设置返回内容为JSONP
public class JsonpAttribute : ActionFilterAttribute { /// <summary> /// 在执行操作结果后更改返回结果 /// < ...
- 利用Microsoft.Practices.Unity的拦截技术,实现.NET中的AOP
1.记住这个单词的意思:Interception(拦截) 2.首先说一下原理和背景 原理:所谓的AOP就是面向切面编程,这里不多说,百度搜索. 目的:个人认为是为了解耦,部分代码跟业务代码分离,业务代 ...
- RabbitMQ 参数们的Power “续”
参数中的 arguments 之前讲参数的一些作用的时候,忽略了最后一个字典类型的参数,因为这个参数是大有文章的,值得单独进出来说道说道. 这时,就不得不打开我们的 Web UI管理系统了,可以看到在 ...
- Nginx编译配置杂记
1.http://nginx.org/download/nginx-1.6.3.tar.gz 2. [root@track nginx-1.6.3]#./configure --prefix=/usr ...
- BZOJ1491: [NOI2007]社交网络
传送门 最短路计数问题.因为数据量非常小($N \leq 100$),所以Floyd随便搞搞就行了. $f[i][j]$表示路径长度,$g[i][j]$表示最短路方案数. 先跑一遍裸的Floyd,然后 ...