目录
1. 引言
2.概念
1.Model
1.职责
2.实现
3.和Controller通信
1.Contrller直接访问Model
2.通过委托(Delegate)模式
3.通知
4.KVO
4.设计的建议
2.View
1.职责
2.实现
3.和Controller通信
1. 目标-动作(Target-Action)模式
2. 委托(Delegate)模式
3. 通知(Notification)模式
4. KVC 和 KVO(Key-Value Coding 和 Key-Value Observing)
5. Block 回调
3.Controller
1.职责
2.实现
3.通信
3. MVC设计模式的优缺点
1.优点
2. 缺点
4. 实践中的建议
1. 引言
MVC(Model-View-Controller)是一种常见的软件设计模式,旨在将应用程序的逻辑、用户界面和输入进行分离。
在iOS开发中,MVC帮助开发者组织代码,提高可维护性和可扩展性,使得项目结构更加清晰。
本文将介绍MVC的基本概念、在iOS中的具体实现、优缺点、实践建议,以及其他常见的架构模式。
2.概念
MVC的全称和基本概念:MVC即Model-View-Controller,分别代表模型、视图和控制器三个部分。
各个组件的职责:
- Model:负责数据和业务逻辑,包括数据的获取、保存、处理等。
- View:负责用户界面的展示,直接与用户进行交互。
- Controller:负责协调Model和View,处理用户输入,并将其转化为对Model的操作或View的更新。
在我们面试的过程中,MVC基本上是面试必问的基础性的问题。笔者总结了两点:MVC就是Controller拿Model的数据区更新View,同时Model和View之间是不互相通信的,它们之间的联系是通过Controller来桥接。
下图是斯坦福大学网上iOS公开课中的关于MVC的讲解,感觉总结的非常不错。
图1.MVC设计模式图
下面逐个分析下MVC中的每一个模块。
1.Model
Model是数据模型的意思,在iOS的设计模式中,主要负责处理数据和业务逻辑。
1.职责
Model的主要职责如下:
1.数据存储:Model负责存储应用程序的数据。数据可以来自本地存储、网络请求或其他外部数据源。
2.数据操作:Model包含操作数据的方法,例如创建、读取、更新和删除(CRUD)操作。
3.业务逻辑:Model还负责处理应用程序的业务逻辑,例如数据验证、计算和转换等。
4.数据通知:当数据发生变化时,Model可以通过通知或回调机制通知Controller,以便更新视图。
2.实现
在实际的开发过程中,Model通常是一个或者多个类,这些类可以代表数据库中的表、网络请求的响应或其他数据结构
下面是一个简单的Model类示例,演示了如何定义和操作Model:
// User.h
#import <Foundation/Foundation.h>
@interface User : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;
- (void)updateName:(NSString *)newName;
@end
// User.m
#import "User.h"
@implementation User
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
self = [super init];
if (self) {
_name = name;
_age = age;
}
return self;
}
- (void)updateName:(NSString *)newName {
_name = newName;
// 通知Controller数据已更新
[[NSNotificationCenter defaultCenter] postNotificationName:@"UserUpdatedNotification" object:self];
}
@end
在这个示例中,User类是一个简单的Model类,包含用户的姓名和年龄。它还提供了一个方法来更新用户的姓名,并在更新后通过通知机制通知Controller。
3.和Controller通信
Model通常不会直接与View通信,而是通过Controller进行中介。Controller负责监听Model的数据变化,并相应地更新View。
Model和Controller中的通信方式有以下几种方式:
1.Contrller直接访问Model
Controller可以直接访问model的属性和方法。
Controller可以直接访问和操作Model的属性和方法,以获取或更新数据。这是最直接和常见的方式。
我们以下面的更新App版本号为例:
图1.更新app版本号
因为Controller直接持有Model,因此我们直接可以直接修改model数据:
#pragma mark -- AboutViewDelegate
- (void)didTapUpdateButton{
self.aboutModel.appVersion = [NSString stringWithFormat:@"%ld.%ld.%ld",[self generateRandomNumber:10],[self generateRandomNumber:99],[self generateRandomNumber:99]];
[self.aboutView configureWithModel:self.aboutModel];
}
- (NSInteger)generateRandomNumber:(NSInteger)random {
return arc4random_uniform(10) + 1; // 生成1到10之间的随机数
}
完整代码如下:
#import "AboutViewController.h"
#import "AboutView.h"
#import "AboutModel.h"
#import "Masonry.h"
@interface AboutViewController ()<AboutViewDelegate>
@property (nonatomic, strong) AboutView *aboutView;
@property (nonatomic, strong) AboutModel *aboutModel;
@end
@implementation AboutViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.title = @"关于";
[self setupModel];
[self setupView];
}
- (void)setupModel {
// 假设我们从某处获取了应用的名称和版本号
NSString *appName = @"myApp";
NSString *appVersion = @"1.0.0";
self.aboutModel = [[AboutModel alloc] initWithAppName:appName appVersion:appVersion];
}
- (void)setupView {
self.aboutView = [[AboutView alloc] initWithFrame:self.view.bounds];
self.aboutView.delegate = self;
[self.view addSubview:self.aboutView];
[self.aboutView configureWithModel:self.aboutModel];
}
#pragma mark -- AboutViewDelegate
- (void)didTapUpdateButton{
self.aboutModel.appVersion = [NSString stringWithFormat:@"%ld.%ld.%ld",[self generateRandomNumber:10],[self generateRandomNumber:99],[self generateRandomNumber:99]];
[self.aboutView configureWithModel:self.aboutModel];
}
- (NSInteger)generateRandomNumber:(NSInteger)random {
return arc4random_uniform(10) + 1; // 生成1到10之间的随机数
}
@end
2.通过委托(Delegate)模式
Model可以定义一个协议(protocol),Controller遵循这个协议并实现其方法。Model通过委托将数据变化通知给Controller,Controller则通过协议的方法来响应这些变化。
还以上面的demo为例,我们还可以把更新的方法传递到Controller,让controller去处理。
#import "AboutViewController.h"
#import "AboutView.h"
#import "AboutModel.h"
#import "Masonry.h"
@interface AboutViewController ()<AboutViewDelegate,AboutModelDelegate>
@property (nonatomic, strong) AboutView *aboutView;
@property (nonatomic, strong) AboutModel *aboutModel;
@end
@implementation AboutViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.title = @"关于";
[self setupModel];
[self setupView];
}
- (void)setupModel {
// 假设我们从某处获取了应用的名称和版本号
NSString *appName = @"myApp";
NSString *appVersion = @"1.0.0";
self.aboutModel = [[AboutModel alloc] initWithAppName:appName appVersion:appVersion];
self.aboutModel.delegate = self;
}
- (void)setupView {
self.aboutView = [[AboutView alloc] initWithFrame:self.view.bounds];
self.aboutView.delegate = self;
[self.view addSubview:self.aboutView];
[self.aboutView configureWithModel:self.aboutModel];
}
#pragma mark -- AboutViewDelegate
- (void)didTapUpdateButton{
NSLog(@"didTapUpdateButton");
[self.aboutModel updateAppVersion:[NSString stringWithFormat:@"%ld.%ld.%ld",[self generateRandomNumber:10],[self generateRandomNumber:99],[self generateRandomNumber:99]]];
}
#pragma mark -- AboutModelDelegate
- (void)didUpdateAppVersion:(NSString *)appVersion{
NSLog(@"AboutModelDelegate");
[self.aboutView configureWithModel:self.aboutModel];
}
- (NSInteger)generateRandomNumber:(NSInteger)random {
return arc4random_uniform(10) + 1; // 生成1到10之间的随机数
}
@end
3.通知
iOS的通知机制(NSNotificationCenter)允许在应用程序的不同部分之间发送消息。Controller可以监听特定的通知,以便在Model发出通知时执行相应的操作。
// MyModel.h
#import <Foundation/Foundation.h>
@interface MyModel : NSObject
@property (nonatomic, strong) NSString *data;
- (void)updateData:(NSString *)newData;
@end
// MyModel.m
#import "MyModel.h"
@implementation MyModel
- (void)updateData:(NSString *)newData {
_data = newData;
[[NSNotificationCenter defaultCenter] postNotificationName:@"DataUpdatedNotification" object:newData];
}
@end
// MyViewController.m
#import "MyViewController.h"
#import "MyModel.h"
@implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.myModel = [[MyModel alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDataUpdated:) name:@"DataUpdatedNotification" object:nil];
[self.myModel updateData:@"New Data"];
}
- (void)handleDataUpdated:(NSNotification *)notification {
NSString *updatedData = (NSString *)notification.object;
NSLog(@"Model data updated: %@", updatedData);
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end
4.KVO
KVO是一种观察模式,允许对象监听其他对象属性的变化。Controller可以监听Model的属性变化,以便在属性发生变化时进行相应的处理。
// MyModel.h
#import <Foundation/Foundation.h>
@interface MyModel : NSObject
@property (nonatomic, strong) NSString *data;
@end
// MyModel.m
#import "MyModel.h"
@implementation MyModel
- (instancetype)init {
self = [super init];
if (self) {
_data = @"Hello, MVC!";
}
return self;
}
@end
// MyViewController.m
#import "MyViewController.h"
#import "MyModel.h"
@implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.myModel = [[MyModel alloc] init];
[self.myModel addObserver:self forKeyPath:@"data" options:NSKeyValueObservingOptionNew context:nil];
self.myModel.data = @"Updated Data";
}
// 处理属性变化
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"data"]) {
NSLog(@"Model data changed: %@", change[NSKeyValueChangeNewKey]);
}
}
- (void)dealloc {
[self.myModel removeObserver:self forKeyPath:@"data"];
}
@end
4.设计的建议
- 保持Model的独立性:Model不应该直接依赖View或Controller。这样可以使Model更加独立和可复用。
- 使用协议和委托:可以使用协议和委托来定义Model与Controller的交互接口,增强代码的灵活性和可维护性。
- 使用数据持久化框架:在处理复杂数据模型时,可以使用Core Data、Realm等数据持久化框架来简化数据管理和存储。
- 注意线程安全:在多线程环境下操作Model时,需要注意线程安全,避免数据竞争和不一致问题。
2.View
1.职责
view是视图的意思。在iOS MVC(Model-View-Controller)设计模式中,View 负责管理应用程序的用户界面(UI)部分。它是用户与应用程序进行交互的界面层,负责显示数据和响应用户操作。以下是对 View 的详细介绍:
- 展示数据:View 负责根据 Model 提供的数据来展示用户界面。它不直接处理数据的逻辑,只是按照 Controller 提供的数据进行展示。View 的任务是将数据直观地呈现给用户。
- 相应用户输入:View 负责处理用户的输入,如点击按钮、滑动屏幕等操作,并将这些事件传递给 Controller 进行处理。View 本身不包含业务逻辑,而是将事件传递给负责逻辑处理的 Controller。
- 独立于业务逻辑:View 不应包含应用程序的业务逻辑或数据处理逻辑。它与 Model 之间没有直接的交互,所有的数据交互都通过 Controller 来完成。这种独立性使得 View 可以很容易地进行修改或替换,而不会影响到应用程序的其他部分。
2.实现
在 iOS 应用程序中,View 的典型组件包括:UIView、UILabel、UIButton、UIImageView、UITableView等。
3.和Controller通信
1. 目标-动作(Target-Action)模式
目标-动作模式是 iOS 中常用的一种通信方式,通常用于处理用户界面控件(如按钮、开关、滑块等)的事件。当用户与控件交互时,控件会向其指定的目标(通常是 Controller)发送一个动作消息。
// 在View中设置目标和动作
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
[button setTitle:@"Press Me" forState:UIControlStateNormal];
[button addTarget:self action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];// Controller 中实现动作方法
- (void)buttonPressed:(UIButton *)sender {
NSLog(@"Button was pressed");
}
2. 委托(Delegate)模式
托模式是一种常见的设计模式,允许一个对象向另一个对象通知发生的事件。在 iOS 中,许多 UIKit 组件都使用委托模式,例如 UITableView 和 UICollectionView。Controller 通常会作为 View 的委托对象,以便处理 View 的事件。
// 自定义View中的委托协议
@protocol CustomViewDelegate <NSObject>
- (void)customViewDidSomething:(UIView *)view;
@end@interface CustomView : UIView
@property (nonatomic, weak) id<CustomViewDelegate> delegate;
@end@implementation CustomView
- (void)doSomething {
[self.delegate customViewDidSomething:self];
}
@end// 在Controller中实现委托方法
@interface ViewController () <CustomViewDelegate>
@end@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
CustomView *customView = [[CustomView alloc] init];
customView.delegate = self;
[self.view addSubview:customView];
}- (void)customViewDidSomething:(UIView *)view {
NSLog(@"CustomView did something");
}
@end
3. 通知(Notification)模式
通知中心(NSNotificationCenter)是一种广播机制,允许对象通过发布和观察通知进行通信。通知是一对多的通信机制,适用于需要向多个对象广播信息的情况。
// 发送通知
[[NSNotificationCenter defaultCenter] postNotificationName:@"CustomNotification" object:nil];// 观察通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@"CustomNotification" object:nil];// 处理通知的方法
- (void)handleNotification:(NSNotification *)notification {
NSLog(@"Received CustomNotification");
}// 移除观察者
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
4. KVC 和 KVO(Key-Value Coding 和 Key-Value Observing)
KVC 和 KVO 是 Cocoa 提供的动态属性访问和观察机制。KVC 允许通过字符串键来访问对象的属性,KVO 允许对象观察其他对象属性的变化。
// Model类
@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@end@implementation Person
@end// Controller中使用KVO观察属性变化
@interface ViewController ()
@property (nonatomic, strong) Person *person;
@end@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.person = [[Person alloc] init];
[self.person 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(@"Person's name changed to %@", change[NSKeyValueChangeNewKey]);
}
}// 移除观察者
- (void)dealloc {
[self.person removeObserver:self forKeyPath:@"name"];
}
@end
5. Block 回调
Block 回调是一种轻量级的替代委托模式的方法。它允许在执行某些操作后,调用一个代码块(block)来处理结果。Block 回调简化了委托模式,不需要定义协议和委托对象。
// 自定义View类使用Block回调
@interface CustomView : UIView
@property (nonatomic, copy) void (^buttonPressedBlock)(void);
@end@implementation CustomView
- (instancetype)init {
self = [super init];
if (self) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
[button setTitle:@"Press Me" forState:UIControlStateNormal];
[button addTarget:self action:@selector(buttonPressed) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:button];
}
return self;
}- (void)buttonPressed {
if (self.buttonPressedBlock) {
self.buttonPressedBlock();
}
}
@end// 在Controller中设置Block回调
@interface ViewController ()
@end@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
CustomView *customView = [[CustomView alloc] init];
customView.buttonPressedBlock = ^{
NSLog(@"Button was pressed in CustomView");
};
[self.view addSubview:customView];
}
@end
3.Controller
1.职责
在 iOS 的 MVC(Model-View-Controller)设计模式中,Controller 是连接 Model 和 View 的中介,负责处理应用程序的业务逻辑和用户交互。Controller 的主要职责是从 Model 中获取数据,更新 View,并响应用户输入。以下是对 Controller 的详细介绍:
1.管理视图:
Controller 负责创建和配置 View,并将 Model 中的数据传递给 View 进行显示。当 Model 的数据发生变化时,Controller 也会通知 View 进行相应的更新。
2.处理用户输入:
Controller 响应用户在 View 上的各种操作,例如点击按钮、滑动手势等。它会根据用户的输入更新 Model,并通过 View 来反映这些变化。
3.业务逻辑:
Controller 包含应用程序的业务逻辑和数据处理逻辑。它从 Model 获取数据、处理数据,并将处理结果传递给 View。
3.中介作用:
Controller 充当 Model 和 View 之间的中介。它负责协调这两者之间的通信,使得 View 不直接与 Model 交互,从而保持两者的解耦。
2.实现
在 iOS 开发中,UIViewController 是 Controller 的基类。每个 UIViewController 都管理一个 UIView,并与该视图相关联的 Model 进行交互。
3.通信
Controller和Model以及View之间的通信可以看上面的章节。
3. MVC设计模式的优缺点
1.优点
1.易于测试和维护
各个组件独立,便于单独测试和维护。
2.代码复用性强
View和Model可以在不同的Controller中重复使用,提高代码复用性。
2. 缺点
1.可能导致Controller过于臃肿
在处理复杂逻辑时,Controller可能变得过于庞大,难以维护。
2.View和Controller之间的耦合度较
由于Controller直接管理View,导致二者耦合度较高,不利于独立开发和测试。
3.复杂项目中的可扩展性问题
在大型项目中,MVC可能难以满足复杂需求,需要引入其他设计模式进行补充。
4. 实践中的建议
1.如何避免Controller臃肿
使用Helper类或Extensions分担部分逻辑。
将业务逻辑移到Model层,尽量保持Controller的简洁。
2.减少View和Controller的耦合
使用协议和委托模式来解耦。
利用通知和KVO(Key-Value Observing)机制实现低耦合的数据传递。
3.代码组织和文件结构的最佳实践:
将Model、View、Controller分别放在不同的文件夹中。
使用命名空间或模块化方式组织代码。