当前位置: 首页 > news >正文

「iOS」————设计架构


架构模式

iOS开发中,常用的架构模式有以下几种:

  1. MVC(Model-View-Controller)模式:是 iOS 开发中最常见的架构模式。在 MVC 模式中,Model 负责数据处理和业务逻辑,View 负责界面展示,Controller 负责协调 Model 和 View 之间的交互。虽然 MVC 模式简单易懂,但在复杂项目中可能导致 Controller 过于臃肿,难以维护。

  2. MVVM(Model-View-ViewModel)模式:MVVM 是一种基于数据绑定的架构模式,将 View 和 Model 之间的耦合度降低。ViewModel 负责处理业务逻辑和数据转换,通过数据绑定将数据展示在 View 上。iOS 中常用的 MVVM 框架有 ReactiveCocoa 和 RxSwift。

  3. MVP(Model-View-Presenter)模式:MVP 模式将 View 和 Model 解耦,Presenter 负责处理业务逻辑和更新 View。Presenter 与 View 之间通过接口进行通信,降低了 View 对业务逻辑的依赖。MVP 模式通常用于需要单元测试的项目中。

此外还有两种架构模式:

  • VIPER(View-Interactor-Presenter-Entity-Routing)模式:VIPER 是一种更加复杂的架构模式,将应用拆分为多个模块,每个模块分别对应于 VIPER 中的不同角色。View 负责展示界面,Interactor 负责业务逻辑和数据处理,Presenter 负责协调 View 和 Interactor,Entity 是数据模型,Routing 负责页面之间的导航。VIPER 模式适用于大型、复杂的项目,能够提高代码的可维护性和可测试性。

  • Clean Architecture:Clean Architecture 是一种关注业务逻辑和数据流的架构模式,将应用分为不同的层级,包括 Entity、UseCase、Repository、Presenter、ViewModel 等。Clean Architecture 提倡将业务逻辑和框架相关的代码分离,使得代码更具可测试性和可维护性。

MVC模式

由三部分组成:

  • 模型(Model):定义数据结构、数据加工与存储。
  • 视图(View):视图内容展示、Touch事件交互。
  • 控制器(Controller):处理业务逻辑、页面生命周期管理。

请添加图片描述

特点:

  • Model和View两者不直接通信,通过C层(Controller)实现信息传递。

  • Model 通过 Notification 和 KVO 机制通知给 Controller。(当Model的变化频繁时。)

    • 进行网络请求等操作
  • View 通过 Target/Action、delegate 和 dataSource 三种机制与 Controller 进行通信。

    • 只设置UI,注意如果cell的话,也是在View层定义,然后用Model在View层设置数据,但是Model是Controller给的,View不涉及Model的获取,只负责暂时。

对于一些静态的数据,C层可以直接操作Model。但是为了View层的解耦,View层建议通过上述三种机制进行传递。

简而言之:MVC就是Controller拿Model的数据区更新View,同时Model和View层之间是不互相通信的,它们之间的联系是通过Controller进行桥接的。

MVVM模式(Model-View-ViewModel)

由于MVC进过不断迭代,Controller会显得十分臃肿,为了给Controller瘦身,衍生出了新的架构模式MVVM架构。

  • Model: 数据层,业务逻辑处理、数据控制(本地数据、网络加载数据)。

  • ViewController/View: 展示层,显示用户可见得视图控件、与用户交互事件。界面的生命周期控制和业务间切换控制。(这一次与MVC的View相同)

    • MVVM将ViewController视作View
  • ViewModel: 数据模型,是组织生成和维护的视图数据层。在这一层开发者对从后端获取的 Model 数据进行转换处理,做二次封装,以生成符合 View 层使用预期的视图数据模型。

通讯关系:

Model和View层不通讯,ViewModel作为桥梁,沟通ViewController和Model。

请添加图片描述

  • 通过这张图,我们可以看出:当View|Controller有用户交互,就通过响应告诉ViewModel,然后Model改变,而ViewModel会监听到Model的改变然后通过Success回调来更新UI。

注意事项:

  • View 引用ViewModel ,但反过来不行 (即不要在ViewModel中引入#import UIKit.h,任何视图本身的引用都不应该放在ViewModel中)
  • ViewModel 可以引用Model,但反过来不行
MVVM中数据的双向绑定

双向绑定是 MVVM 架构的核心特性,指的是 View 和 ViewModel 之间的自动同步机制:

正向绑定:View->viewModel

  • 使用Block传值等方式完成正向的数据绑定

反向绑定:viewModel->View

  • 可以使用KVO等反向传值方式。

MVVM模式中心思想是数据实现双向绑定,业务逻辑与视图的分离。

MVVM的ModelView没有交互,交互移步到ViewModelView持有ViewModelViewModel持有Model,反过来持有的话View容易直接跟Model容易产生耦合,这样就失去了架构的意义;

MVVM 在使用当中,通常还会利用双向绑定技术,使得 Model 变化时,ViewModel 会自动更新,而 ViewModel 变化时,View 也会自动变化。所以,MVVM 模式有些时候又被称作:model-view-binder 模式。

  1. 数据流动方向
  • Model → ViewModel → View

    • Model 层数据变化(如 name/email/age 改变)

    • ViewModel 通过 KVO 监听 Model 属性,自动更新自己的 displayXXX 属性

    • View 通过 KVO 监听 ViewModel 的 displayXXX 属性,自动刷新 UI

  • View → ViewModel → Model

    • 用户在 View 上操作(如输入、点击按钮)

    • View 调用 ViewModel 的方法(如 updateUserName:)

    • ViewModel 修改 Model 属性

    • 触发上面 Model → ViewModel → View 的链式自动更新

2. 双向绑定的本质

  • View 只和 ViewModel 交互,不直接操作 Model

  • ViewModel 作为桥梁,既能响应 Model 的变化,也能响应 View 的操作

  • KVO(或其他数据绑定机制)实现自动同步

缺点:

  • 数据绑定使得Bug 很难被调试。你看到界面异常了,有可能是你 View 的代码有 Bug,也可能是 Model 的代码有问题。数据绑定使得一个位置的 Bug 被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。
  • 对于过大的项目,数据绑定需要花费更多的内存。
ReactiveCocoa

函数式编程(Functional Programming)和响应式编程(React Programming)也是当前很火的两个概念,它们的结合可以很方便地实现数据的绑定。于是,在 iOS 编程中,ReactiveCocoa 横空出世了,它的概念都非常 新,包括:

  • 函数式编程(Functional Programming),函数也变成一等公民了,可以拥有和对象同样的功能,例如当成参数传递,当作返回值等。看看 Swift 语言带来的众多函数式编程的特性,就你知道这多 Cool 了。
  • 响应式编程(React Programming),原来我们基于事件(Event)的处理方式都弱了,现在是基于输入(在 ReactiveCocoa 里叫 Signal)的处理方式。输入还可以通过函数式编程进行各种 Combine 或 Filter,尽显各种灵活的处理。
  • 无状态(Stateless),状态是函数的魔鬼,无状态使得函数能更好地测试。
  • 不可修改(Immutable),数据都是不可修改的,使得软件逻辑简单,也可以更好地测试。

MVP模式

MVP(Model-View-Presenter)是一种在 iOS 开发中常用的架构模式,类似于 MVC 和 MVVM,但在 MVP 中,视图和模型之间的通信通过 Presenter 层进行,从而实现视图和模型的解耦。它由三部分组成:

  • 模型(Model):
    • 负责管理数据和业务逻辑
  • 视图(View):
    • 视图负责展示数据和用户交互,与MVC中的视图类似
    • 视图不包含业务逻辑,只负责展示数据和向Presenter发送用户操作的信息。
  • 主持人(Presenter):
    • Presenter 是 MVP 中的关键部分,负责处理视图的逻辑和用户交互。
    • Presenter 从模型中获取数据,并将数据转换为视图可用的格式,然后更新视图。
    • Presenter 不依赖于视图,通过接口与视图进行通信。

就是Presenter作为Model和View的中间人,从model层获取数据之后传给view,使得view和model没有耦合。

主要用法为:View层抽象为协议,然后Presenter层继承这个协议,Controller中引入Presenter实现这个协议的具体方法。Controller为View的延伸,只展示这个View,和使用时间交互逻辑。具体处理逻辑在Presenter中

使用中:

每个功能模块都有presenter这个文件夹,对每个模块的presenter都是为这个模块服务,我们可以把请求、储存数据的活动放在这里。并且在这层presenter中处理model数据。为了使controller得到的数据能直接使用,可以多写一个protocol,来承上启下,HomeViewProtocol就为了这个产生。

总结

MVP 架构下,Presenter 是连接 View 和 Model 的桥梁,所有业务逻辑都在 Presenter,Model 负责数据,View 只负责展示。

上述作为MV(X)的设计模式,都把一个应用分为三类:

  • Models–负责主要的数据或者操作数据的数据访问层,可以想象 Perspn 和 PersonDataProvider 类。
  • Views–负责展示层(GUI),对于iOS环境可以联想一下以 UI 开头的所有类。
  • Controller/Presenter/ViewModel–负责协调 Model 和 View,通常根据用户在View上的动作在Model上作出对应的更改,同时将更改的信息返回到View上。

MV(X)的区别

  • MVC:Controller 负责业务逻辑,View 和 Model 直接交互较多,耦合度高。

  • MVP:Presenter 负责业务逻辑,View 只暴露接口,Model 只管数据,三者解耦更彻底。

  • MVVM:ViewModel 负责业务逻辑和数据转换,View 通过数据绑定自动更新。

设计模式

设计模式分为三大类:创建式模式、结构型模式、行为型模式

创建型模式

这类模式关注于对象的创建过程,提供了创建对象的不同方式,以降低耦合度并提高代码的灵活性。

  • 单例模式:确保一个类只有一个实例,并提供一个全局访问点

  • 工厂模式:提供一个创建对象的接口,但让子类决定实例化哪一个类。

  • 抽象工厂模式:为创建一系列相关或相互依赖的对象提供一个接口,而无需指定它们具体的类。

  • 建造者模式:分步骤构建复杂对象,用户只需指定类型和内容,不需知道内部构造细节。

  • 原型模式:通过克隆已有对象来创建新对象,减少创建成本。

结构型模式

这类模式关注于如何组合类和对象以获得更大的结构,同时保持结构的灵活和高效。

  • 适配器模式:将一个类的接口转换成客户期望的另一个接口。

  • 桥接模式:将抽象部分与实现部分分离,使它们可以独立变化。

  • 装饰模式:动态地给一个对象添加一些额外的职责,而不改变其结构。

  • 组合模式:将对象组合成树形结构以表示“整体-部分”层次结构。

  • 外观模式:为一组复杂的子系统提供一个统一的接口,以简化高层模块的使用。

行为型模式

这类模式关注于对象间的通信,以及职责的分配。

  • 责任链模式:将请求沿着链传递,直到有对象处理它。

  • 命令模式:将请求封装成对象,以便使用不同的请求、队列请求、日志请求等。

  • 迭代器模式:提供一种方法顺序访问聚合对象的元素,而又不暴露其内部表示。

  • 观察者模式:定义对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。

  • 策略模式:定义一系列算法,将它们一个个封装起来,并使它们可以相互替换。

  • 模板方法模式:定义一个操作中的算法骨架,而将一些步骤延迟到子类中实现。

  • 访问者模式:在不改变数据结构的前提下,增加作用于这些元素的新操作。

以下是iOS设计中常用的六种模式:

单例模式

确保一个类只有一个实例,并提供一个全局访问方法。

分为懒汉模式(使用时才加载)和饿汉模式(一开始就加载)

一般使用加锁或者GCD实现,用GCD实现更好。

委托模式

“委托模式是一种常用的回调机制。委托方(如 UITableView)定义协议并持有 delegate 属性,代理方(如
ViewController)实现协议方法并将自己赋值为
delegate。这样,委托方在需要时通过协议回调代理方,实现事件的解耦和灵活扩展。”

允许一个对象(委托方)将某些任务交给另一个对象(委托对象)处理。这是一种对象间通信的模式。用于事件回调或数据请求。

实现步骤:

  1. 定义委托协议(Protocol)
  2. 在委托方中声明一个弱引用(weak)的delegate属性
  3. 在需要时向delegate发送信息
  4. 委托对象实现协议方法。
// 定义协议
@protocol TaskDelegate <NSObject>
- (void)taskDidComplete;
@end
// 委托方
@interface TaskHandler : NSObject
@property (nonatomic, weak) id<TaskDelegate> delegate;
- (void)startTask;
@end
@implementation TaskHandler
- (void)startTask {// 任务完成后回调[self.delegate taskDidComplete];
}
@end
// 委托对象
@interface ViewController : UIViewController <TaskDelegate>
@end
@implementation ViewController
- (void)viewDidLoad {[super viewDidLoad];TaskHandler *handler = [[TaskHandler alloc] init];handler.delegate = self;
}
- (void)taskDidComplete {NSLog(@"Task completed!");
}
@end

观察者模式

定义对象间的一种一对多的依赖关系,当一个对象状态改变时,所有依赖它的对象都得到通知并自动更新。

通过KVO或者通知实现。

  • KVO (Key-Value Observing): 内建于NSObject,用于监听属性变化。
  • NSNotificationCenter: 广播机制,任意对象可以发送通知,多个观察者可以监听。

示例:

KVO

// 被观察对象
@interface ObservedObject : NSObject
@property (nonatomic, strong) NSString *name;
@end
// 观察者
[self.observedObject addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
// 实现回调
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {if ([keyPath isEqualToString:@"name"]) {NSLog(@"Name changed to: %@", change[NSKeyValueChangeNewKey]);}
}
// 移除观察者(在dealloc中)
- (void)dealloc {[self.observedObject removeObserver:self forKeyPath:@"name"];
}

NSNotificationCenter示例

// 发送通知
[[NSNotificationCenter defaultCenter] postNotificationName:@"DataUpdated" object:nil];
// 监听通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDataUpdated:) name:@"DataUpdated" object:nil];
// 处理通知
- (void)handleDataUpdated:(NSNotification *)notification {// 更新UI或数据
}
// 移除监听(在dealloc中)
- (void)dealloc {[[NSNotificationCenter defaultCenter] removeObserver:self];
}

工厂方法模式

定义一个创建对象的接口,但让子类决定实例化哪一个类。工厂方法使得一个类的实例化延迟到其子类。

不指定具体类也能创建一系列相关或依赖的对象。

在属性那块使用到,set方法

// 抽象产品
@interface Product : NSObject
@end
// 具体产品A
@interface ProductA : Product
@end
// 具体产品B
@interface ProductB : Product
@end
// 工厂基类
@interface Factory : NSObject
+ (Product *)createProduct;
@end
@implementation Factory
+ (Product *)createProduct {// 默认实现,或者抛出异常(因为子类必须重写)[NSException raise:@"Abstract Method" format:@"Subclasses must override"];return nil;
}
@end
// 具体工厂A
@interface FactoryA : Factory
@end
@implementation FactoryA
+ (Product *)createProduct {return [[ProductA alloc] init];
}
@end
// 具体工厂B
@interface FactoryB : Factory
@end
@implementation FactoryB
+ (Product *)createProduct {return [[ProductB alloc] init];
}
@end
// 使用
Product *productA = [FactoryA createProduct];
Product *productB = [FactoryB createProduct];

策略模式

定义一系列算法,将每个算法封装起来,并使它们可以互相替换。策略模式让算法独立于使用它的客户端而变化。

使用场景:需要动态切换算法或行为(如排序算法、支付方式)。

实现:

// 策略接口
@protocol PaymentStrategy <NSObject>
- (void)payAmount:(NSInteger)amount;
@end
// 具体策略:信用卡支付
@interface CreditCardPayment : NSObject <PaymentStrategy>
@end
@implementation CreditCardPayment
- (void)payAmount:(NSInteger)amount {NSLog(@"Paid %ld via Credit Card", amount);
}
@end
// 具体策略:支付宝支付
@interface AlipayPayment : NSObject <PaymentStrategy>
@end
@implementation AlipayPayment
- (void)payAmount:(NSInteger)amount {NSLog(@"Paid %ld via Alipay", amount);
}
@end
// 上下文(使用策略)
@interface PaymentContext : NSObject
@property (nonatomic, strong) id<PaymentStrategy> strategy;
- (void)executePayment:(NSInteger)amount;
@end
@implementation PaymentContext
- (void)executePayment:(NSInteger)amount {[self.strategy payAmount:amount];
}
@end
// 使用
PaymentContext *context = [[PaymentContext alloc] init];
context.strategy = [[CreditCardPayment alloc] init];
[context executePayment:100]; // 使用信用卡支付
context.strategy = [[AlipayPayment alloc] init];
[context executePayment:200]; // 使用支付宝支付

组合模式

将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

使用场景:表示树形结构(如UIView层次、菜单树)。

// 组件接口
@protocol Component <NSObject>
- (void)operation;
@end
// 叶子节点(无子节点)
@interface Leaf : NSObject <Component>
@end
@implementation Leaf
- (void)operation {NSLog(@"Leaf operation");
}
@end
// 复合节点(可包含子节点)
@interface Composite : NSObject <Component>
@property (nonatomic, strong) NSMutableArray<id<Component>> *children;
- (void)addComponent:(id<Component>)component;
- (void)removeComponent:(id<Component>)component;
@end
@implementation Composite
- (instancetype)init {if (self = [super init]) {_children = [NSMutableArray array];}return self;
}
- (void)addComponent:(id<Component>)component {[self.children addObject:component];
}
- (void)removeComponent:(id<Component>)component {[self.children removeObject:component];
}
- (void)operation {// 先执行自身操作NSLog(@"Composite operation");// 再执行所有子节点的操作for (id<Component> child in self.children) {[child operation];}
}
@end
// 使用
Composite *root = [[Composite alloc] init];
Composite *branch1 = [[Composite alloc] init];
Leaf *leaf1 = [[Leaf alloc] init];
Leaf *leaf2 = [[Leaf alloc] init];
[root addComponent:branch1];
[root addComponent:leaf1];
[branch1 addComponent:leaf2];
[root operation]; // 递归调用所有节点的operation方法

设计原则

  • 单一职责原则

一个类只负责一项职责(功能),不要把不相关的功能都塞进一个类。也就是说一个类应当专注于做好一件事情。

例子:单例,UIVIew和CALayer单一职能。

  • 开放封闭原则

软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。 也就是当需要改变行为时,应该通过扩展而不是修改已有的代码来实现。

优点:允许在不修改原有代码的情况下扩展新功能,减少引入bug的风险

例子:UITableVIew里面的自定义cell,不改动系统代码,通过扩展修改

  • 里氏替换原则

子类对象必须能够替换父类对象,并且程序逻辑不变。同理,父类对象可以无差别地使用子类对象。

例子:子类重写父类方法时,不能改变原有方法的语义。UIView 的子类都能安全地替换 UIView。

  • 接口隔离原则

不应该强迫客户依赖它们不需要的接口。一个类对另一个类的依赖应该建立在最小的接口上。接口应该尽量细化,客户端只依赖它实际需要的办法。

例子:iOS 的 delegate 协议通常拆分为多个小协议(如 UITableViewDelegate、UITableViewDataSource)。

  • 依赖倒置原则

高层模块不应该依赖低层模块,二者都应该依赖于抽象(接口/协议);抽象不应该依赖细节,细节应该依赖抽象。

例子:通过协议(interface)编程,而不是直接依赖具体类。iOS 的数据源和代理都是通过协议实现的。

  • 迪米特原则/最小知识原则

一个对象应该对其他对象有尽可能少的了解(只与直接朋友通信)。

不要链式访问(a.b.c.d),使用方法访问。

  • 合成复用原则

优先使用对象组合(has-a),而不是类继承(is-a)来实现代码复用。

合成
合成是指一个总体对依托他而存在的关系,如一个人对他的房子和家具。该关系依赖性不强,比如人没了,这个关系就自然消失了。
聚合

聚合是比合成关系更强的一种依赖关系,如有一台汽车,汽车对引擎、轮胎的关系就是聚合关系。这些关系就是带有聚合性质的。车没了,该车的引擎和轮胎自然也没了。在我们的设计中,这种关系不应该频繁出现,因为这样会增大设计的耦合度。

工厂模式介绍

简单工厂模式就是用协议,一个init方法判断传入的字符串

工厂方法模式就是继承,用不同具体子类初始化。

抽象工厂模式:

类似于上面两种模式的合成。首先通过一个根类工厂,使用简单工厂模式判断,进入不同的工厂。在到具体的工厂,用工厂方法模式,初始化不同的工件。

http://www.lryc.cn/news/620572.html

相关文章:

  • iOS 26 一键登录失效:三大运营商 SDK 无法正常获取手机号
  • iOS性能监控新方法多版本对比与趋势分析实战指南
  • iOS混淆工具有哪些?游戏 App 防护下的混淆与加固全攻略
  • 网络通信---Axios
  • iOS App TestFlight 上架全流程案例,从 0 到 1 完成内测分发
  • Docker 部署:Web SSH、RDP、VNC 多协议全能远程管理工具
  • 零基础数据结构与算法——第七章:算法实践与工程应用-搜索引擎
  • 洗浴中心泡池水过滤系统原理深度解析与工程实践
  • 数智先锋 | 告别运维黑盒!豪鹏科技×Bonree ONE构建全栈智能可观测体系
  • 【网络】TCP/UDP总结复盘
  • Ollama如何分别使用2张H100GPU和4张A100部署GPT-OSS-120B全指南:硬件配置与负载均衡实战
  • PostgreSQL——触发器
  • Nginx学习笔记(八)—— Nginx缓存集成
  • GraphRAG查询(Query)流程实现原理分析
  • Unity人形角色IK优化指南
  • C++-setmap详解
  • 图灵测试:人工智能的“行为主义判据”与哲学争议
  • Elastic 获得 2025 年 Google Cloud DORA “以 AI 构建未来架构” 奖
  • 认知系统的架构: 认知残余三角形、认知主体意识 和认知演进金字塔(腾讯元宝)
  • Vue Vant应用-数据懒加载
  • Linux入门指南:基础开发工具---yum/apt
  • 分享一个基于Hadoop+spark的超市销售数据分析与可视化系统,超市顾客消费行为分析系统的设计与实现
  • 2025年大模型安全岗的面试汇总(题目+回答)
  • 使用Applications Manager进行 Apache Solr 监控
  • LeetCode 37.解数独:回溯法在二维网格中的应用与剪枝策略
  • 考公VS考研,拼哪个性价比高?
  • 考研408《计算机组成原理》复习笔记,第四章(1)——指令系统概念(指令字长、N地址指令、定长和变长操作码)
  • 微软发布五大AI Agent设计模式 推动企业自动化革新
  • 使用 Rust 进行 Web 自动化入门
  • Playwright初学指南 (3):深入解析交互操作