iOS组件化 方案 实现

iOS组件化

  • 组件化的原因
  • 现在流行的组件化方案
    • 方案一、url-block (基于 URL Router)
    • 方案二、protocol
      • 调用方式解读
    • 方案三、target-action
      • 调用方式解读
  • gitHub代码链接参考

组件化的原因

  • 模块间解耦
  • 模块重用
  • 提高团队协作开发效率
  • 单元测试

当项目App处于起步阶段、各个需求模块趋于成熟稳定的过程中,组件化也许并没有那么迫切,甚至考虑组件化的架构可能会影响开发效率和需求迭代。而当项目迭代到一定时期之后,便会出现一些相对独立的业务功能模块,而团队的规模也会随着项目迭代逐渐增长,这便是中小型应用考虑组件化的时机了。

为了更好的分工协作,团队会安排团队成员各自维护一个相对独立的业务组件。这个时候我们引入组件化方案,一是为了解除组件之间相互引用的代码硬依赖,二是为了规范组件之间的通信接口; 让各个组件对外都提供一个黑盒服务,而组件工程本身可以独立开发测试,减少沟通和维护成本,提高效率。

进一步发展,当团队涉及到转型或者有了新的立项之后,一个团队会开始维护多个项目App,而多个项目App的需求模块往往存在一定的交叉,而这个时候组件化给我们的帮助会更大,我只需要将之前的多个业务组件模块在新的主App中进行组装即可快速迭代出下一个全新App。

现在流行的组件化方案

方案一、url-block (基于 URL Router)

通过在启动时(load方法中)注册组件提供的服务,把调用组件使用的url和组件提供的服务block对应起来,保存到内存中。在使用组件的服务时,通过url找到对应的block,然后通过block回调获取服务。

url-block的架构图如下:
在这里插入图片描述

代码说明

首先要在 + (void)load 中进行注册

[LYXRouter registerURLPattern:@"lyx://foo/bar" toHandler:^(NSDictionary *routerParameters) {// create view controller// push view controllerstringWithFormat:@"routerParameters:%@", routerParameters]];}];

然后调用的时候传入url:

    [MGJRouter openURL:@"lyx://foo/bar"];

代码内部会通过传入的url,拿到对应的block,然后进行调用。
之后block中的回调内部 就会执行 跳转页面的代码了。

使用url-block的方案的确可以组建间的解耦,但是还是存在其它明显的问题,比如:

  • 需要在内存中维护url-block的表,组件多了可能会有内存问题
  • url的参数传递受到限制,只能传递常规的字符串参数,无法传递非常规参数,如UIImage、NSData等类型
  • 没有区分本地调用和远程调用的情况,尤其是远程调用,会因为url参数受限,导致一些功能受限
  • 组件本身依赖了中间件,且分散注册使的耦合较多

方案二、protocol

是通过protocol定义服务接口,组件通过实现该接口来提供接口定义的服务,具体实现就是把protocolclass做一个映射,同时在内存中保存一张映射表,使用的时候,就通过protocol找到对应的class来获取需要的服务。

protocol - class 架构图如下:
请添加图片描述

调用方式解读

注册:

[ModuleManager registerClass:ClassA forProtocol:ProtocolA]

调用:

[ModuleManager classForProtocol:ProtocolA]

具体流程可参考如下:
创建中间件
1、创建 组件化的中间件manager,中间件 提供注册机制的方法,通过字典存储 procotol-class
2、中间件manager 提供 通过协议获取class的方法

- (void)registServiceProvide:(id)provide forProcotol:(Protocol *)procotol;- (id)serviceProvideForProcotol:(Protocol *)procotol;

业务侧提供协议 + 遵循协议的类
1、提供协议,协议中提供 需要的方法

@protocol ProductDetailServiceProcotol <NSObject>
- (UIViewController *)productDetailViewControllerWithProductId:(NSString *)productId;@end

2、创建类,类遵循协议,实现协议方法,方法内部提供想要的 代码
3、创建的类中的+ (void)load方法,内部 调用 中间件 注册protocol-calss

@interface ProductDetailServiceProvide ()<ProductDetailServiceProcotol>@end@implementation ProductDetailServiceProvide//load 方法会在加载类的时候就被调用,也就是 ios 应用启动的时候,就会加载所有的类,就会调用每个类的 + load 方法
+ (void)load
{[[ProcotolManager sharedManger] registServiceProvide:[[self alloc] init] forProcotol:@protocol(ProductDetailServiceProcotol)];
}#pragma mark - ProductDetailServiceProcotol- (UIViewController *)productDetailViewControllerWithProductId:(NSString *)productId
{ProcotolProductDetailViewController *detailVC = [[ProcotolProductDetailViewController alloc] init];detailVC.productId = productId;return detailVC;
}@end

调用
通过协议找到类,然后调用协议 方法「即:最终走的是 协议找到的那个class的方法」

id<ProductOrderServiceProcotol> servicePrivide = [[ProcotolManager sharedManger] serviceProvideForProcotol:@protocol(ProductOrderServiceProcotol)];UIViewController *orderVC = [servicePrivide productOrderWithProductId:self.productId];[self.navigationController pushViewController:orderVC animated:YES];

这种方案确实解决了方案一中无法传递非常规参数的问题,使得组件间的调用更为方便,但是它依然没有解决组件依赖中间件的问题、内存中维护映射表的问题、组件的分散调用的问题。

方案三、target-action

重点词: 分类 runtime
该方案 中间件是通过runtime来调用组件的服务,是真正意义上的解耦,也是该方案最核心的地方。具体实施过程是给组件封装一层target对象来对外提供服务,不会对原来组件造成入侵;然后,通过实现中间件的category来提供服务给调用者,这样使用者只需要依赖中间件,而组件则不需要依赖中间件。

调用方式解读

创建中间件
中间件内部实现是通过runtime 拿到类和方法,进行调用的

@interface SYMediator : NSObject
+(instancetype)shareInstance;- (id)performTargetName:(NSString *)targetName actionName:(NSString *)actionName param:(NSDictionary *)dicParam;
@end

创建中间件的分类
分类中 的方法 调用 中间件的performTarget... 方法,来实现最终的方法调用。


#import "SYMediator+BookVC.h"static NSString *const kBookTarget = @"BookTarget";
static NSString *const kBookAction = @"bookVCWithParam";@implementation SYMediator (BookVC)
- (UIViewController *)bookViewControllerWithDicParam:(NSDictionary *)dicParm
{UIViewController *vc = [self performTargetName:kBookTarget actionName:kBookAction param:dicParm];if ([vc isKindOfClass:[UIViewController class]]) {return vc;} else {return [[UIViewController alloc] init];}
}
@end

创建target

组件封装一层target对象来对外提供服务

@implementation BookTarget
- (UIViewController *)bookVCWithParam:(NSDictionary *)dicParm
{BookViewController *bookVC = [[BookViewController alloc] init];bookVC.bookName = dicParm[@"bookName"];bookVC.bookId = dicParm[@"bookid"];return bookVC;
}
@end

调用

NSDictionary *dicParm = @{@"bookName" : @"降龙十八掌",@"bookid" : @"sy0001"};//第一种方式(有category)UIViewController *bookVC = [[SYMediator shareInstance] bookViewControllerWithDicParam:dicParm];[self.navigationController pushViewController:bookVC animated:YES];

gitHub代码链接参考

https://github.com/liyuunxiangGit/iOS-modularization

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/20114.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络原理-四

一、续 当窗口大小为0,意味着缓冲区满了,此时发送方,就因该暂停发送,发送方会周期性的除法 " 窗口探测包 " ,并不携带载荷,这样的包对于业务不产生影响,只是为了触发ACK,一旦查询出来的结果是非0,缓冲区右有空间了,发送方就可以继续发送. 二、拥塞控制 要限制发送方…

【AI+知识库问答】沉浸式体验了解 AI知识库问答fastGPT

之前写过一篇文章 【AI本地知识库】个人整理的几种常见本地知识库技术方案 &#xff0c; 由于当时主要是针对AI本地知识库&#xff0c; 所以没列fastGPT。 最近经常刷到fastGPT&#xff0c;这里单独水一篇。 FastGPT 是一个基于 LLM 大语言模型的知识库问答系统&#xff0c;…

Github 2024-06-01 开源项目日报Top10

根据Github Trendings的统计,今日(2024-06-01统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目5Jupyter Notebook项目2TypeScript项目1Go项目1Shell项目1Lua项目1Kong:云原生API网关与AI能力 创建周期:3482 天开发语言:Lua协议…

云原生架构相关技术_4.服务网格

1.技术特点 服务网格&#xff08;ServiceMesh&#xff09;是分布式应用在微服务软件架构之上发展起来的新技术&#xff0c;旨在将那些微服务间的连接、安全、流量控制和可观测等通用功能下沉为平台基础设施&#xff0c;实现应用与平台基础设施的解耦。这个解耦意味着开发者无需…

Chisel入门——在windows下vscode搭建|部署Scala2.13.3开发环境|用Chisel点亮FPGA小灯等实验

文章目录 前言一、vscode搭建scala开发环境1.1 安装Scala官方插件1.2 创建hello_world.scala文件1.3 确认java的版本(博主使用的是1.8)1.4 下载Scala Windows版本的二进制文件1.5 配置环境变量1.6 交互模式测试一下1.7 vscode运行scala 二、windows安装sbt2.1 下载sbt2.2 设置环…

函数递归及具体例子(持续更新)

递归就是函数自己调用自己 求n的阶乘 n! n * (n - 1)! 直到n为1或者0的时候为止 举个例子 int Fun(int n) {if (n < 0){return 1;}else{return n * Fun(n - 1);} }int main() {int n 0;scanf("%d", &n);int ret Fun(n);printf("%d\n", ret…

安装Kubernetes v3 ----以docker的方式部署

以docker的方式部署 docker run -d \ --restartunless-stopped \ --namekuboard \ -p 80:80/tcp \ -p 10081:10081/tcp \ -e KUBOARD_ENDPOINT"http://192.168.136.55:80" \ -e KUBOARD_AGENT_SERVER_TCP_PORT"10081" \ -v /root/kuboard-data:/data \ e…

springboot中抽象类无法注入到ioc容器

1、背景 在写代码时&#xff0c;发现service接口有两个实现类&#xff0c;并且两个实现类中没有对类名重命名&#xff0c;属性注入的时候也没有使用byName或Qualifier&#xff0c;正确情况下会发生多实现报错的问题&#xff0c;以前对这个问题进行解析过。 2、调试过程 我想…

【设计模式】创建型-建造者模式

前言 在面向对象的软件开发中&#xff0c;构建复杂对象时经常会遇到许多挑战。一种常见的解决方案是使用设计模式&#xff0c;其中建造者模式是一个强大而灵活的选择。本文将深入探讨建造者模式的原理、结构、优点以及如何在实际项目中应用它。 一、复杂的对象 public class…

飞凌嵌入式FET3568/3568J-C核心板现已适配OpenHarmony4.1

近日&#xff0c;飞凌嵌入式为FET3568/3568J-C核心板适配了OpenHarmony4.1系统&#xff0c;新系统的加持使核心板在兼容性、稳定性与安全性等方面都得到进一步提升&#xff0c;不仅为FET3568/3568J-C核心板赋予了更强大的功能&#xff0c;也为开发者们提供了更加广阔的创新空间…

每日一练编程题:今天是【接口,多态】

设计程序 : 电脑类的属性USB接口数组 : 有3个usb插口电脑类的功能 : 通过接口插入外设 (u盘,麦克风,键盘等) addUSB(USB usb) { }开机 要求: 电脑开机前,先启动外设关机 要求: 电脑关机前,先关闭外设 外设类(u盘,麦克风,键盘等) 功能 : 启动 关闭 USB接口 定义usb设备的统一…

python多种方式 保留小数点位数(附Demo)

目录 前言1. 字符串格式2. round函数3. Decimal模块4. numpy库5. Demo 前言 在Python中&#xff0c;保留小数点后特定位数可以通过多种方式实现 以下是几种常见的方法&#xff0c;并附上相应的代码示例&#xff1a; 使用字符串格式化&#xff08;String Formatting&#xff…

Ubuntu20.04 Mysql基本操作知识

#Mysql基本知识 运行环境Ubuntu20.04 1.开启mysql服务 sytemctl start mysql不然&#xff0c;命令行进入myql交互行提交命令后&#xff0c;就会出现4200错误。 2.显示所有数据库 SHOW DATABASES;注意复数s&#xff0c;毕竟很多数据库 3.新建数据库test CREATE DATABASE …

【学习笔记】计算机组成原理(九+十)

控制单元的功能 文章目录 控制单元的功能9.1 微操作命令的分析9.1.1 取指周期9.1.2 间址周期9.1.3 执行周期9.1.4 中断周期 9.2 控制单元的功能9.2.1 控制单元的外特性9.2.2 控制信号举例9.2.3 多级时序系统9.2.4 控制方式 控制单元的设计10.1 组合逻辑设计10.1.1 组合逻辑控制…

LabVIEW与Simulink的通信及调用方式

LabVIEW和Simulink可以通过多种方式进行通信和集成&#xff0c;实现数据交互和功能调用。常见的通信方式包括TCP/IP、UDP、共享内存等&#xff0c;此外还可以利用MATLAB Script Node和S-Function等直接调用对方的功能。这些方法使得LabVIEW和Simulink能够协同工作&#xff0c;充…

[Algorithm][动态规划][子序列问题][最长递增子序列的个数][最长数对链]详细讲解

目录 1.最长递增子序列的个数1.题目链接2.算法原理详解3.代码实现 2.最长数对链1.题目链接2.算法原理详解3.代码实现 1.最长递增子序列的个数 1.题目链接 最长递增子序列的个数 2.算法原理详解 注意&#xff1a;本题思路和思维方式及用到的方法很值得考究&#xff0c;个人感…

dubbo复习:(18)服务端Filter

用来在服务响应返回到客户端之前进行额外处理。 一、定义Filter package cn.edu.tju.config;import org.apache.dubbo.rpc.Filter; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Invocation; import org.apache.du…

大量path计算优化方案

1.影响path基础属性数据做key缓存&#xff0c;缓存的path应去除坐标变换&#xff0c;归一化。基础属性应满足CAB, BC-A 2.高频path操作以&#xff08;keykey操作&#xff09;做新key缓存。 3.高频修改高级属性&#xff0c;以新key属性变更做新key缓存。 4.key与id做中转映射&am…

ad18学习笔记20:焊盘设置Solder Mask Expansion(阻焊层延伸)

【AD18新手入门】从零开始制造自己的PCB_ad18教程-CSDN博客 Altium Designer绘制焊盘孔&#xff08;Pad孔&#xff09;封装库的技巧&#xff0c;包括原理图封装和PCB封装_哔哩哔哩_bilibili 默认的焊盘中间是有个过孔的&#xff0c;单层焊盘&#xff08;表贴烛盘&#xff09;…

工作流 Activiti7 初始

文章目录 ☃️1.1 Activiti 介绍☃️1.2 Activiti 开发流程☃️1.3 BPMN 2.0 规范是什么☃️1.4 BPMN 2.0 基本流程符号❄️❄️1.4.1 事件 Event❄️❄️1.4.2 活动❄️❄️1.4.3 网关 Gateway ☃️1.5 Activiti API 服务接口❄️❄️1.5.1 核心Service接口及其获取 ☃️1.1 A…