利用runtime来给组件化项目appdelegate减负

1,018 阅读3分钟

前言

本文主要依赖我写的两个工具

1.UCRuntimeKit:这个小工具主要是为了能够利用字符串+runtime动态的调用方法.经过了500多条单元测试,基本满足所有的使用场景,安全,且支持向调用方抛出error,但不会crash.在这里它主要是为了向各个组件中的delegate转发多参数方法用的.

2.UCAppDelegateReduce.这个小工具依赖上面的UCRuntimeKit来做消息转发.

实现原理

  • 1.先将项目中的delegate与框架中的UCAppDelegateRealize实现的22个方法进行交换,如果appdelegate没实现这个方法则不会交换,交换完成后不影响之前的逻辑,依旧会先调用原先appdelegate的逻辑,如果这22个方法没有包含你需要转发给组件的方法的话支持扩展.

  • 2.当delegate接收到调用时,会自动来到UCAppDelegateRealize,这里在会根据位移枚举的配置来对各个组件调用,这个调用过程依赖UCRuntimeKit,十分安全,不会crash.

使用

可以利用cocoapod进行导入,pod 'UCAppDelegateReduce'

Objc中的使用

ObjcDemo

  • 1.新建一个Objc类,例如我这里取名叫AppDelegateExchange

  • 2.在load类方法里进行下面的配置,就可以了

      + (void)load {
    
      UCAppDelegateMethodExchangeManager *manager = [UCAppDelegateMethodExchangeManager share];
      // sendMessageType 是位移枚举,可以选择自己想给子模块派发的方法
      UCAppDelegateConfigModel *model1 = [[UCAppDelegateConfigModel alloc] initWithModuleName:@"UCObjcModule1AppDelegate" sendMessageType:didFinishLaunchingWithOptions];
      UCAppDelegateConfigModel *model2 = [[UCAppDelegateConfigModel alloc] initWithModuleName:@"UCObjcModule2AppDelegate" sendMessageType:handleOpenURL | didFinishLaunchingWithOptions];
      
      [manager startExchangeMethodWithOriginalAppDelegateName:@"UCAppDelegate" newModuleAppDelegateConfigArray:@[model1, model2]];
      }
    

Swift的使用

SwiftDemo

  • 1.新建一个Objc类,例如我这里取名叫AppDelegateExchange

  • 2.要转发的Module中的appdelegate方法前要加@objc,否则不支持动态调用

  • 3.在load类方法里进行下面的配置,就可以了

      + (void)load {
      
      UCAppDelegateMethodExchangeManager *manager = [UCAppDelegateMethodExchangeManager share];
      
      // 这里Objc调用Swift需要加上类似命名空间的前缀,否则找不到这个Swift class
      NSString *swiftModule1Name = [UCMediatorParameterParser getObjcClassName:@"SwiftModule1AppDelegate"];
      // 支持位移枚举
      UCAppDelegateConfigModel *model1 = [[UCAppDelegateConfigModel alloc] initWithModuleName:swiftModule1Name sendMessageType:didFinishLaunchingWithOptions];
      
      NSString *swiftModule2Name = [UCMediatorParameterParser getObjcClassName:@"SwiftModule2AppDelegate"];
      UCAppDelegateConfigModel *model2 = [[UCAppDelegateConfigModel alloc] initWithModuleName:swiftModule2Name sendMessageType:handleOpenURL | didFinishLaunchingWithOptions];
      
      NSString *originalDelegateName = [UCMediatorParameterParser getObjcClassName:@"AppDelegate"];
      [manager startExchangeMethodWithOriginalAppDelegateName:originalDelegateName newModuleAppDelegateConfigArray:@[model1, model2]];
      }
    

UCAppDelegateConfigModel是很灵活的,支持位移枚举配置,配置后不会调用未配置的方法,另外manager的配置在app的生命周期中只应该在最开始load的时候配置一次!配置完成后这里会解析,生成类似这样的map,以代理方法名为key,需要转发的类名集合为value,并将这个map缓存在内存中,方便快速转发给各个组件.

这样你就可以把例如原先写在appdelegate中的开屏配置,分享key配置,支付配置,push路由配置,3Dtouch等等直接写在各自的模块中了,这里的调用顺序也是可以通过配置来决定的.

但是如果这个调用是异步的,例如组件B的一个功能依赖组件B的回调,建议还是把这个写在组件a的delegate实现里.

做完了这些你会发现你原来的delegate基本可以什么都不写了,他已经把业务分散到各个组件中去了.

UT

关于单元测试,这里基本只是测试了调用这个模块的,转发各个模块都是手动测得,判断参数能传入到各个组件就没有更深度的写了,这里的单元测试确实不太好写.