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
743 0
iOS 源码分析(三):MLeaksFinder
|
JSON 数据格式 iOS开发
iOS 源码分析(二):YYModel
iOS 源码分析(二):YYModel
609 0
iOS 源码分析(二):YYModel
|
缓存 算法 安全
iOS-底层原理 06:malloc 源码分析 思路
iOS-底层原理 06:malloc 源码分析 思路
425 0
iOS-底层原理 06:malloc 源码分析 思路
|
iOS开发
iOS-底层原理 04:NSObject的alloc 源码分析
iOS-底层原理 04:NSObject的alloc 源码分析
134 0
iOS-底层原理 04:NSObject的alloc 源码分析
|
存储 算法 安全
iOS-底层原理 02:alloc & init & new 源码分析
iOS-底层原理 02:alloc & init & new 源码分析
144 0
iOS-底层原理 02:alloc & init & new 源码分析
|
18天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
9天前
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
11天前
|
存储 前端开发 Swift
探索iOS开发:从新手到专家的旅程
本文将带您领略iOS开发的奇妙之旅,从基础概念的理解到高级技巧的掌握,逐步深入iOS的世界。文章不仅分享技术知识,还鼓励读者在编程之路上保持好奇心和创新精神,实现个人成长与技术突破。
|
26天前
|
安全 数据处理 Swift
深入探索iOS开发中的Swift语言特性
本文旨在为开发者提供对Swift语言在iOS平台开发的深度理解,涵盖从基础语法到高级特性的全面分析。通过具体案例和代码示例,揭示Swift如何简化编程过程、提高代码效率,并促进iOS应用的创新。文章不仅适合初学者作为入门指南,也适合有经验的开发者深化对Swift语言的认识。
45 9