iOS中的MVVM

2024-08-29 08:48
文章标签 ios mvvm

本文主要是介绍iOS中的MVVM,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

我于 2011 年在 500px 找到自己的第一份 iOS 开发工作。虽然我已经在大学里做了好几年 iOS 外包开发,但这才是我的一个真正的 iOS 开发工作。我被作为唯一的 iOS 开发者被招聘去实现拥有漂亮设计的 iPad 应用。在短短七周里,我们就发布了 1.0 并持续迭代,添加了更多特性,但从本质上,代码库也变得更加复杂了。

有时我感觉就像我不知道在做什么。虽然我知道自己的设计模式——就像任何好的编程人员那样 —— 但我太接近我在做的产品以至于不能客观地衡量我的架构决策的有效性。当队伍中来了另外一位开发者时,我意识到我们陷入困境了。

从没听过 MVC ?有人称之为 Massive View Controller(重量级视图控制器),这就是我们那时候的感觉。我不打算介绍令人汗颜的细节,但说实在的,如果我不得不再次重来一次,我绝对会做出不同的决策。

我会修改一个关键架构,并将其带入我从那时起就在开发的各种应用,即使用一种叫做 Model-View-ViewModel 的架构替换 Model-View-Controller。

所以,MVVM 到底是什么?与其专注于说明 MVVM 的来历,不如让我们看一个典型的 iOS 是如何构建的,并从那里了解 MVVM:

Typical Model-View-Controller setup

我们看到的是一个典型的 MVC 设置。Model 呈现数据,View 呈现用户界面,而 View Controller 调节它两者之间的交互。Cool!

稍微考虑一下,虽然 View 和 View Controller 是技术上不同的组件,但它们几乎总是手牵手在一起,成对的。你什么时候看到一个 View 能够与不同 View Controller 配对?或者反过来?所以,为什么不正规化它们的连接呢?

Intermediate

这更准确地描述了你可能已经编写的 MVC 代码。但它并没有做太多事情来解决 iOS 应用中日益增长的重量级视图控制器的问题。在典型的 MVC 应用里,许多逻辑被放在 View Controller 里。它们中的一些确实属于 View Controller,但更多的是所谓的“表示逻辑(presentation logic)”,以 MVVM 属术语来说,就是那些将 Model 数据转换为 View 可以呈现的东西的事情,例如将一个NSDate 转换为一个格式化过的 NSString

我们的图解里缺少某些东西,那些使我们可以把所有表示逻辑放进去的东西。我们打算将其称为 “View Model” —— 它位于 View/Controller 与 Model 之间:

Model-View-ViewModel

看起好多了!这个图解准确地描述了什么是 MVVM:一个 MVC 的增强版,我们正式连接了视图和控制器,并将表示逻辑从 Controller 移出放到一个新的对象里,即 View Model。MVVM 听起来很复杂,但它本质上就是一个精心优化的 MVC 架构,而 MVC 你早已熟悉。

现在我们知道了什么是 MVVM,但为什么我们会想要去使用它呢?在 iOS 上使用 MVVM 的动机,对我来说,无论如何,就是它能减少 View Controller 的复杂性并使得表示逻辑更易于测试。通过一些例子,我们将看到它如何达到这些目标。

此处有三个重点是我希望你看完本文能带走的:

  • MVVM 可以兼容你当下使用的 MVC 架构。
  • MVVM 增加你的应用的可测试性。
  • MVVM 配合一个绑定机制效果最好。

如我们之前所见,MVVM 基本上就是 MVC 的改进版,所以很容易就能看到它如何被整合到现有使用典型 MVC 架构的应用中。让我们看一个简单的 Person Model 以及相应的 View Controller:

@interface Person : NSObject- (instancetype)initwithSalutation:(NSString *)salutation firstName:(NSString *)firstName lastName:(NSString *)lastName birthdate:(NSDate *)birthdate;@property (nonatomic, readonly) NSString *salutation;
@property (nonatomic, readonly) NSString *firstName;
@property (nonatomic, readonly) NSString *lastName;
@property (nonatomic, readonly) NSDate *birthdate;@end

Cool!现在我们假设我们有一个 PersonViewController ,在 viewDidLoad 里,只需要基于它的 model 属性设置一些 Label 即可。

- (void)viewDidLoad {[super viewDidLoad];if (self.model.salutation.length > 0) {self.nameLabel.text = [NSString stringWithFormat:@"%@ %@ %@", self.model.salutation, self.model.firstName, self.model.lastName];} else {self.nameLabel.text = [NSString stringWithFormat:@"%@ %@", self.model.firstName, self.model.lastName];}NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];[dateFormatter setDateFormat:@"EEEE MMMM d, yyyy"];self.birthdateLabel.text = [dateFormatter stringFromDate:model.birthdate];
}

这全都直截了当,标准的 MVC。现在来看看我们如何用一个 View Model 来增强它。

@interface PersonViewModel : NSObject- (instancetype)initWithPerson:(Person *)person;@property (nonatomic, readonly) Person *person;@property (nonatomic, readonly) NSString *nameText;
@property (nonatomic, readonly) NSString *birthdateText;@end

我们的 View Model 的实现大概如下:

@implementation PersonViewModel- (instancetype)initWithPerson:(Person *)person {self = [super init];if (!self) return nil;_person = person;if (person.salutation.length > 0) {_nameText = [NSString stringWithFormat:@"%@ %@ %@", self.person.salutation, self.person.firstName, self.person.lastName];} else {_nameText = [NSString stringWithFormat:@"%@ %@", self.person.firstName, self.person.lastName];}NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];[dateFormatter setDateFormat:@"EEEE MMMM d, yyyy"];_birthdateText = [dateFormatter stringFromDate:person.birthdate];return self;
}@end

Cool!我们已经将 viewDidLoad 中的表示逻辑放入我们的 View Model 里了。此时,我们新的 viewDidLoad 就会非常轻量:

- (void)viewDidLoad {[super viewDidLoad];self.nameLabel.text = self.viewModel.nameText;self.birthdateLabel.text = self.viewModel.birthdateText;
}

所以,如你所见,并没有对我们的 MVC 架构做太多改变。还是同样的代码,只不过移动了位置。它与 MVC 兼容,带来更轻量的 View Controllers。

可测试,嗯?是怎样?好吧,View Controller 是出了名的难以测试,因为它们做了太多事情。在 MVVM 里,我们试着尽可能多的将代码移入 View Model 里。测试 View Controller 就变得容易多了,因为它们不再做一大堆事情,并且 View Model 也非常易于测试。让我们来看看:

SpecBegin(Person)NSString *salutation = @"Dr.";NSString *firstName = @"first";NSString *lastName = @"last";NSDate *birthdate = [NSDate dateWithTimeIntervalSince1970:0];it (@"should use the salutation available. ", ^{Person *person = [[Person alloc] initWithSalutation:salutation firstName:firstName lastName:lastName birthdate:birthdate];PersonViewModel *viewModel = [[PersonViewModel alloc] initWithPerson:person];expect(viewModel.nameText).to.equal(@"Dr. first last");});it (@"should not use an unavailable salutation. ", ^{Person *person = [[Person alloc] initWithSalutation:nil firstName:firstName lastName:lastName birthdate:birthdate];PersonViewModel *viewModel = [[PersonViewModel alloc] initWithPerson:person];expect(viewModel.nameText).to.equal(@"first last");});it (@"should use the correct date format. ", ^{Person *person = [[Person alloc] initWithSalutation:nil firstName:firstName lastName:lastName birthdate:birthdate];PersonViewModel *viewModel = [[PersonViewModel alloc] initWithPerson:person];expect(viewModel.birthdateText).to.equal(@"Thursday January 1, 1970");});
SpecEnd

如果我们没有将这个逻辑移入 View Model,我们将不得不实例化一个完整的 View Controller 以及伴随的 View,然后去比较我们 View 中 Lable 的值。这样做不只是会变成一个麻烦的间接层,而且它只代表了一个十分脆弱的测试。现在,我们可以按意愿自由地修改视图层级而不必担心破坏我们的单元测试。使用 MVVM 带来的对于测试的好处非常清晰,甚至从这个简单的例子来看也可见一斑,而在有更复杂的表示逻辑的情况下,这个好处会更加明显。

注意到在这个简单的例子中, Model 是不可变的,所以我们可以只在初始化的时候指定我们 View Model 的属性。对于可变 Model,我们还需要使用一些绑定机制,这样 View Model 就能在背后的 Model 改变时更新自身的属性。此外,一旦 View Model 上的 Model 发生改变,那 View 的属性也需要更新。Model 的改变应该级联向下通过 View Model 进入 View。

在 OS X 上,我们可以使用 Cocoa 绑定,但在 iOS 上我们并没有这样好的配置可用。我们想到了 KVO(Key-Value Observation),而且它确实做了很伟大的工作。然而,对于一个简单的绑定都需要很大的样板代码,更不用说有许多属性需要绑定了。作为替代,我个人喜欢使用 ReactiveCocoa,但 MVVM 并未强制我们使用 ReactiveCocoa。MVVM 是一个伟大的典范,它自身独立,只是在有一个良好的绑定框架时做得更好。

这篇关于iOS中的MVVM的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1117403

相关文章

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

【iOS】MVC模式

MVC模式 MVC模式MVC模式demo MVC模式 MVC模式全称为model(模型)view(视图)controller(控制器),他分为三个不同的层分别负责不同的职责。 View:该层用于存放视图,该层中我们可以对页面及控件进行布局。Model:模型一般都拥有很好的可复用性,在该层中,我们可以统一管理一些数据。Controlller:该层充当一个CPU的功能,即该应用程序

MVC(Model-View-Controller)和MVVM(Model-View-ViewModel)

1、MVC MVC(Model-View-Controller) 是一种常用的架构模式,用于分离应用程序的逻辑、数据和展示。它通过三个核心组件(模型、视图和控制器)将应用程序的业务逻辑与用户界面隔离,促进代码的可维护性、可扩展性和模块化。在 MVC 模式中,各组件可以与多种设计模式结合使用,以增强灵活性和可维护性。以下是 MVC 各组件与常见设计模式的关系和作用: 1. Model(模型)

iOS剪贴板同步到Windows剪贴板(无需安装软件的方案)

摘要 剪贴板同步能够提高很多的效率,免去复制、发送、复制、粘贴的步骤,只需要在手机上复制,就可以直接在电脑上 ctrl+v 粘贴,这方面在 Apple 设备中是做的非常好的,Apple 设备之间的剪贴板同步功能(Universal Clipboard)确实非常方便,它可以在 iPhone、iPad 和 Mac 之间无缝传输剪贴板内容,从而大大提高工作效率。 但是,iPhone 如何和 Wind

iOS项目发布提交出现invalid code signing entitlements错误。

1、进入开发者账号,选择App IDs,找到自己项目对应的AppId,点击进去编辑, 2、看下错误提示出现  --Specifically, value "CVYZ6723728.*" for key "com.apple.developer.ubiquity-container-identifiers" in XX is not supported.-- 这样的错误提示 将ubiquity

我的第一次份实习工作-iOS实习生-第三个月

第三个月 这个月有一个考核项目,是一个电子书阅读器,组长说很重要,是我的实习考核项目。 我的项目XTReader,这是我参考网上的一些代码,和模仿咪咕阅读做的,功能还不完善,数据的部分是用聚合数据做的。要收费的。   还有阅读页面,基本功能实现了一下。使用了autolayout,自适应布局,也是第一次用网络,第一次用数据库,第一次用自动布局。还有很多不足。 做了一周多,有个问题一直没

我的第一次份实习工作-iOS实习生-公司使用过的软件

bittorrentsync 素材,文件同步软件 cornerstone svn 软件开发合作 mark man 测量坐标的软件 SQLLite Manager 数据库操作软件

我的第一次份实习工作-iOS实习生-第二个月

第二个月 来公司过了一个月了。每天早上9点上班,到晚上6.30下班,上下班要指纹打卡,第一个月忘了打卡好多次(),然后还要去补打卡单。公司这边还安排了,工资卡办理,招商银行卡。开了一次新员工大会,认识了公司的一些过往,公司的要求等,还加了一下公司的企业QQ,还有其他的羽毛球群,篮球群。我加了下羽毛球群,也去打了一两次。第二个月的感受,感觉跟组里面的交流跟沟通都好少,基本上还有好多人不认识。想想也

我的第一次份实习工作-iOS实习生-第一个月

实习时间:2015-08-20 到 2015-12-25  实习公司;福建天棣互联有限公司 实习岗位:iOS开发实习生 第一个月: 第一天来公司,前台报道后,人资带我去我工作的地方。到了那,就由一个组长带我,当时还没有我的办公桌,组长在第三排给我找了一个位置,擦了下桌子,把旁边的准备的电脑帮我装了下,因为学的是iOS,实习生就只能用黑苹果了,这是我实习用的电脑。 帮我装了一下电脑后,开机

iOS如何隐藏系统状态栏

这里主要说明一下iOS7系统给状态栏的适配及隐藏带来的改变。 变化一: 不隐藏状态栏的情况下,StatusBar会直接显示在当前页面上,当前页面的会延伸到 StatusBar下方,顶到最上头。 这种显示方式在iOS7上是无法改变的,也无法通过设置或者配置类达到iOS6的状态栏效果。       所以在iOS7上进行页面布局的时候要考虑