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中的功能组合模式。