iOS 源码分析(一):CTMediator

简介: iOS 源码分析(一):CTMediator

本文的主要目的是分析CTMediator以及其使用


CTMediator简介


CTMediatorcasatwy开源的一个三方组件通讯框架,下图是通过CTMediator通讯的整体架构图示

image.png

优点:


  • 组件仅通过Action暴露可调用接口,模块与模块之间的接口被固化在了Target-Action这一层,避免了实施组件化的改造过程中,对Business的侵入,同时也提高了组件化接口的可维护性。
  • 方便传递各种类型的参数。
  • 本地组件通讯为远程组件通讯提供服务
  • 利用 category 可以明确声明的接口,进行编译检查
  • 实现方式属于轻量级


缺点:


  • 需要给每一个模块创建对应的 target和 mediator 分类,模块化代码时较为繁琐
  • 在 category 中仍然使用了字符串硬编码,内部使用字典传参
  • 无法保证所调用的目标模块一定存在,运行时才能发现错误


CTMediator原理


解耦原理


  • 通过中介模式,将CTMediator类作为各个模块的中心,各个模块以CTMediator分类的形式扩展功能,CTMediator分类中提供服务
  • 分类中的函数具体去调用target-action,target和action需要以字符串硬编码的形式,如果模块比较多,提供服务比较多,那么字符串的硬编码需要时间维护。
  • 去model化思想:如果A模块向B模块传递信息,推荐参数使用基本数据类型,而不是以model形式提供,否则A模块依赖同一个model,B模块也依赖同一个model,这样模块间还是存在相互依赖,没有达到真正完全耦合。


实现原理


  • 基于OC的runtimecategory特性动态获取模块
  • 通过NSClassFromString动态获取类,并创建实例
  • 通过NSSelectorFromString动态获取SEL
  • 通过performSelector+NSInvocation动态调用方法
  • performSelector响应OC的动态性,将方法的绑定延迟到运行时,即在编译阶段不会检测方法的有效性(如果方法不存在也不会报错)。
  • 如果方法名未知可能会引起内存泄漏相关问题,可以通过以下代码忽略此警告
//如果方法名称是动态不确定的,会有如下提示
⚠️ PerformSelector may cause a leak because its selector is unknown
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    [self performSelector:selector];
#pragma clang diagnostic pop
  • performSelector提供动态执行方法的能力
  • NSInvocation提供了消息调用的能力
  • 方法调用的本质是消息发送,即底层调用的是objc_msgSend,可以从objc源码得到验证

- (id)performSelector:(SEL)sel withObject:(id)obj {
  if (!sel) [self doesNotRecognizeSelector:sel];
  return ((id(*)(id, SEL, id))objc_msgSend)(self, sel, obj);
}


源码分析


通过阅读CTMediator的源码可知,以下是源码的通讯过程图示

image.png

以上是组件化方案的一个简化版架构描述,主要是基于Mediator模式Target-Action模式,中间采用了runtime来完成调用。这套组件化方案将远程应用调用和本地应用调用做了拆分,而且是由本地应用调用为远程应用调用提供服务


CTMediator调用的方式有两种(伪代码实现)


  • 本地组件化通讯:本地原生模块间的调用
- (id _Nullable )performTarget:action:params:shouldCacheTarget:{
    //拼接类名字符串
    NSString *targetClassString = "Target_" + targetNaem;
    //从缓存中获取类
    NSObject *target = [self safeFetchCachedTarget:targetClassString]
    if (缓存中没有target){
        Class targetClass = NSClassFromString(targetClassString);
        target = [[targetClass alloc] init];
    }
    //拼接方法字符串
    NSString *actionString = "Action_" + actionName;
    //生成SEL
    SEL action = NSSelectorFromString(actionString);
    if (target为空){
        处理target为空的情况
    }
    //是否缓存target
    if (shouldCacheTarget) {
        //以key-value的形式缓存
        (key:target, value:targetClassString)
    }
    //判断target是否响应action
    if ([target respondsToSelector:action]) {
        return [self safePerformAction:action target:target params:params];
    }else{
        处理target未响应action的情况
    }
}
- (id)safePerformAction:target:params:{
    //根据action生成签名
    NSMethodSignature* methodSig = [target methodSignatureForSelector:action];
    //根据不同的返回类型,进行封装
    //根据签名对象创建调用对象invocation
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
    invocation设置target、action、params
    //消息调用
    [invocation invoke];
    //提供动态执行方法
    return [target performSelector:action withObject:params];
}
  • 远程组件化通讯:其他app调用,主要是通过openURL的方式,需要在AppDelegate的openUrl代理方法中进行处理
- (id _Nullable)performActionWithUrl:completion:{
    处理url参数 - 遍历并存放到字典中
    处理targetName,目的是为了防止黑客通过远程方式调用本地模块
    [self performTarget:action:params:shouldCacheTarget:];
    回调处理
}


CTMediator使用


传统的界面跳转方式如下,模块间高度耦合


image.png

以下是以Home、Detail模块为例,通过CTMediator的解耦方式

image.png

以下是Demo代码的整体结构

image.png

整体结构


具体的实现代码如下


  • Home模块实现的核心代码如下

- (void)pushDetailAction:(UIButton *)sender{
  UIViewController *vc = [MIM() MIMediator_pushDetail];
  [self.navigationController pushViewController:vc animated:YES];
}
  • Detail模块实现的核心代码如下

- (void)showAlert{
//  [[MIMediator sharedInstance] MIMediator_showAlert];
  [MIM() MIMediator_showAlert]; 
}

可以简化组件化单例调用方式

// 简化调用单例的函数
MIMediator* _Nonnull MIMD(void);
  • 组件化通讯实现的代码如下
//MIMediator+Universal中的实现
- (UIViewController *)MIMediator_pushDetail{
  UIViewController *vc = [self performTarget:kMIMediatorTargetDetail action:kMIMediatorActionPushDetail params:nil shouldCacheTarget:NO];
  if ([vc isKindOfClass:[UIViewController class]]) {
    return vc;
  }else{
    return [[UIViewController alloc] init];;
  }
}
- (void)MIMediator_showAlert{
  [self performTarget:kMIMediatorTargetDetail action:kMIMediatorActionShowAlert params:nil shouldCacheTarget:NO];
}
//Target_Detail中的实现
- (UIViewController *)Action_pushToDetail:(NSDictionary *)param{
  DetailViewController *detailVC = [[DetailViewController alloc] init];
  detailVC.title = @"详情页";
  return detailVC;
}
- (id)Action_showAlert:(NSDictionary *)dic{
  UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"alert title" message:@"alert message" preferredStyle:UIAlertControllerStyleAlert];
  [alertVC addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:nil]];
  [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertVC animated:YES completion:nil];
  return nil;
}

参考链接:

相关文章
|
开发者 iOS开发
iOS 源码分析(三):MLeaksFinder
iOS 源码分析(三):MLeaksFinder
730 0
iOS 源码分析(三):MLeaksFinder
|
JSON 数据格式 iOS开发
iOS 源码分析(二):YYModel
iOS 源码分析(二):YYModel
595 0
iOS 源码分析(二):YYModel
|
缓存 算法 安全
iOS-底层原理 06:malloc 源码分析 思路
iOS-底层原理 06:malloc 源码分析 思路
420 0
iOS-底层原理 06:malloc 源码分析 思路
|
iOS开发
iOS-底层原理 04:NSObject的alloc 源码分析
iOS-底层原理 04:NSObject的alloc 源码分析
129 0
iOS-底层原理 04:NSObject的alloc 源码分析
|
存储 算法 安全
iOS-底层原理 02:alloc & init & new 源码分析
iOS-底层原理 02:alloc & init & new 源码分析
142 0
iOS-底层原理 02:alloc & init & new 源码分析
|
1月前
|
Java Android开发 Swift
安卓与iOS开发对比:平台选择对项目成功的影响
【10月更文挑战第4天】在移动应用开发的世界中,选择合适的平台是至关重要的。本文将深入探讨安卓和iOS两大主流平台的开发环境、用户基础、市场份额和开发成本等方面的差异,并分析这些差异如何影响项目的最终成果。通过比较这两个平台的优势与挑战,开发者可以更好地决定哪个平台更适合他们的项目需求。
102 1
|
1月前
|
设计模式 安全 Swift
探索iOS开发:打造你的第一个天气应用
【9月更文挑战第36天】在这篇文章中,我们将一起踏上iOS开发的旅程,从零开始构建一个简单的天气应用。文章将通过通俗易懂的语言,引导你理解iOS开发的基本概念,掌握Swift语言的核心语法,并逐步实现一个具有实际功能的天气应用。我们将遵循“学中做,做中学”的原则,让理论知识和实践操作紧密结合,确保学习过程既高效又有趣。无论你是编程新手还是希望拓展技能的开发者,这篇文章都将为你打开一扇通往iOS开发世界的大门。
|
1月前
|
搜索推荐 IDE API
打造个性化天气应用:iOS开发之旅
【9月更文挑战第35天】在这篇文章中,我们将一起踏上iOS开发的旅程,通过创建一个个性化的天气应用来探索Swift编程语言的魅力和iOS平台的强大功能。无论你是编程新手还是希望扩展你的技能集,这个项目都将为你提供实战经验,帮助你理解从构思到实现一个应用的全过程。让我们开始吧,构建你自己的天气应用,探索更多可能!
62 1
|
2月前
|
IDE Android开发 iOS开发
探索Android与iOS开发的差异:平台选择对项目成功的影响
【9月更文挑战第27天】在移动应用开发的世界中,Android和iOS是两个主要的操作系统平台。每个系统都有其独特的开发环境、工具和用户群体。本文将深入探讨这两个平台的关键差异点,并分析这些差异如何影响应用的性能、用户体验和最终的市场表现。通过对比分析,我们将揭示选择正确的开发平台对于确保项目成功的重要作用。