iOS热修复(动态Framework)

gybddp 8年前

来自: http://finalshares.com/read-6951

上次降到了利用jspatch实现热修复,那么今天我就简单说一下关于动态framework 来进行动态修复;

犹豫存在较复杂的解耦,所以我将整个app写成一个Framework;

1、这里是一个比较简单的app 首先需要写一个管理类去管理分配这些资源,

/**   *  主程序和此动态库的关系枢纽,也就是从“主程序”到“动态库内封装的程序”的入口方法   *   *  @param rootViewController “主程序”中引导   *  @param bundle             此动态库在document文件中的路径,用于xib的加载和图片的加载   */  - (void)bootLoader:(id)rootViewController WithBundle:(NSBundle *)bundle;

实现方法:

- (void)bootLoader:(id)rootViewController WithBundle:(NSBundle *)bundle  {      /*       *初始化第一个管理Controller       *这里的重点是xib文件的加载        通常我们在初始化xib的时候并不是很在意bundle:这个参数,一般情况下都会赋予nil值        其实我们所用到的图片、xib等资源文件都是在程序内部中获取的,也就是我们常用的[NSBundle mainBundle]中获取,所谓的NSBundle本质上就是一个路径,mainBundle指向的是.app下。        而如果我们不指定bundle,则会默认从.app路径下去寻找资源。        不过很显然,我们的动态库是放到“主程序”的document文件下的,所以资源文件是不可能在[NSbundle mainBundle]中获取到的,所以这里我们需要指定bundle参数,这也是传递framework的路径的意义所在       */      _currentBundle = bundle;      [DataCenter shareCenter].bootLoader = bundle;      HZRootViewController *rooter = [[HZRootViewController alloc]initWithNibName:NSStringFromClass([HZRootViewController class]) bundle:bundle];      //保存NSBundle      rooter.bootLoader = bundle;             [rootViewController presentViewController:rooter animated:YES completion:NULL];             [self setRootViewController: [DataCenter shareCenter].uToken ? YES : NO WithRooter:rooter];     }

2、我们需要在app也就是这个程序壳里载入这部分代码

- (void)setRooter  {      NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);      NSString *documentDirectory = nil;      if ([paths count] != 0)          documentDirectory = [paths objectAtIndex:0];             //拼接放到document中的framework路径      NSString *libName = @"HZDelivery.framework";      NSString *destLibPath = [documentDirectory stringByAppendingPathComponent:libName];             //判断一下有没有这个文件的存在 如果没有直接跳出      NSFileManager *manager = [NSFileManager defaultManager];      if (![manager fileExistsAtPath:destLibPath]) {          NSLog(@"There isn't have the file");          return;      }             //复制到程序中      NSError *error = nil;             //使用NSBundle加载动态库      NSBundle *frameworkBundle = [NSBundle bundleWithPath:destLibPath];      if (frameworkBundle && [frameworkBundle load]) {          NSLog(@"bundle load framework success.");      }else {          NSLog(@"bundle load framework err:%@",error);          return;      }             /*       *通过NSClassFromString方式读取类       *PacteraFramework 为动态库中入口类       */      Class pacteraClass = NSClassFromString(@"HZDeliveryManager");      if (!pacteraClass) {          NSLog(@"Unable to get TestDylib class");          return;      }             /*       *初始化方式采用下面的形式        alloc init的形式是行不通的        同样,直接使用PacteraFramework类初始化也是不正确的       *通过- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;        方法调用入口方法(bootLoader:WithBundle:),并传递参数(withObject:self withObject:frameworkBundle)       */      NSObject *pacteraObject = [pacteraClass new];      [pacteraObject performSelector:@selector(bootLoader:WithBundle:) withObject:self withObject:frameworkBundle];  }

在这里推荐做一些后台逻辑去验证文件的完整和正确性。

也可以对生成的Framework进行加密压缩处理。

综上所述:这就可以实现动态的更新包替换,但是更推荐分模块去加载,这样做的好处不用多说,但是存在一定的技术难度和对整个项目的把控,同样不要盲目的去实现,实现计算工程难度。