RxSwift视图模型的剖析

视图控制器中使用每个属性来驱动UI行为。 在此示例中,每个输入和输出都是一个Driver ,因此强烈建议使用Driver ,因为Driver始终在主线程上运行,永远不会完成,并且永远不会产生错误事件。 这使得视图控制器对那些Driver具有非常简单的响应。

将视图模型连接到视图控制器

看一下将这些Driver绑定到UI控件有多么简单:

DisposeBag ,在新版本的RxSwift中,可以使用更好的样式将Disposable插入DisposeBag中。 现在,您可以使用相同的代码块执行以下操作:

视图模型初始化器

关于该视图模型的下一件要注意的事情是,使用格式正确的元组将输入和依赖项传递到初始化程序的简洁方法。

这种样式无疑使视图模型的输入是什么,并且还提供了对任何依赖项的清晰声明。

Zaher在评论中指出,在init方法的主体中,

一切都只是一个定义。 从输入序列到输出序列的纯转换。

该引言总结了MVVM视图模型最重要的一个方面: 输入序列转换为输出序列。

需要注意的另一件事:类上没有其他方法。 一切都在init函数中发生。 相反, SearchResultViewModel是确实具有实例方法的视图模型的示例。

让我们看一下这如何影响init的设计。 因为init方法在初始化所有属性之前无法访问self,并且因为他想在self上使用这些方法来准备imageURL和title输出,所以他被迫为它们提供一个临时值,然后用实际输出覆盖它们。 我不推荐这种方法。

在视图模型上避免实例方法

视图模型上的实例方法只会增加内存泄漏的风险。 这些方法的任何调用都需要引用self。 您必须非常小心, 不要 在闭包内部使用实例方法 。 Zaher不在任何闭包内使用它们,因此SearchResultViewModel没有泄漏。 但是我会争辩说,即使为此使用实例方法也为意外的内存泄漏打开了大门。 如在此重构版本中一样,最好使用文件专用顶级功能。

最终结果是GithubSignupViewModel2.init充当静态函数。 将其输出连接到控件后,不再需要返回的GithubSignupViewModel2实例。 输出将保留在内存中直到被处置,但视图模型实例本身已被释放。 这样可以确保没有内存泄漏。

视图模型可以是不变的结构

GithubSignupViewModel2可以很容易地成为一个struct 。 实际上,将其更改为一个struct ,它仍然可以编译并运行。 由于您只需要一个实例,并且由于其所有属性都是不可变的,因此对于此视图模型而言,实际上更喜欢使用struct

视图模型可以是一个功能

因为我们可以并且应该设计视图模型以在init方法内部完成所有输入到输出的转换,所以我们实际上只需要一个函数。 在下面的要点中,我将视图模型重构为顶级功能。 没课。 没有结构。 只是一个功能。 这是您可以粘贴在RxExample项目中的要点,以替换原始的GitHubSignupViewController2 ,然后编译并运行。 无需更改视图控制器。

我从DJ Mitchell的博客文章“将视图模型建模为函数”中学习了此技术。 他归功于Stephen Celis,请参阅“简化的反应式视图模型”。

TL; DR回顾

实体视图模型的关键点:

  • 函数或init签名中明确标识的输入
  • 将输入流转换成输出流
  • 建议将Driver用于输出
  • 没有实例方法-改用顶级函数
  • 视图模型应在视图控制器的viewDidLoad()末尾viewDidLoad()
  • 强烈建议使用struct或顶级函数,而不要使用类

本文提供了上述建议的示例和理由。 希望您觉得它有用,并欢迎您提供反馈。

如果您喜欢这篇文章,请鼓掌。 如果您想查看我的更多帖子,请关注我或访问我的页面。

下一主题:Swift中的功能组合模式。