避免RxSwift内存泄漏

我最近对RxSwift的优雅充满热情。 我仍在学习使用RxSwift的越来越好的方法,在这篇简短的文章中,我将分享一些我所学到的避免内存泄漏的知识。 我希望这些指针对那些可能遇到相同问题并寻求解决方案的人有用。

首先,有一点背景。 在我们的项目中,我们使用模型视图呈现器(MVP)架构,其中呈现器具有关于UI如何响应输入以及处理与模型对象的所有交互的所有逻辑。 演示者与视图和模型交互,但是模型和视图不直接交互。 注意,视图≠视图控制器。 在我们的项目中,视图是由视图控制器实现的协议,用于向演示者公开特定的接口。

因此,演示者必须同时引用视图和演示者。 该视图对演示者有很强的参考力,因此可以在加载视图后向演示者注册自己,以便在视图卸载后可以释放演示者。 因此,演示者必须对该视图有较弱的引用,以避免保留周期。

我们在项目中创建的每个视图都将在viewDidLoad()末尾调用其演示者进行注册。 演示者可以在此订阅UI中的可观察对象,并可以绑定到UI控件。 这是一个视图控制器向其演示者注册的示例:

演示者的registerView()方法将以一种非常声明性的方式定义所有UI逻辑。 这是一个部分示例:

难题#1-函数参数

在这里,您可以看到演示者如何要求模型使用URL提取图像,以及Observable使用活页夹将Observable绑定到视图中的UIImageView 。 但是,示例代码已经存在问题。 在bind(to: createPostView.posterImageBinder)createPostView参数是对该视图的强引用。 由于该视图对演示者有很强的参考作用,因此从一开始就存在一个保留周期。 视图,演示者和模型将在视图卸载后保留在内存中。 更糟糕的是,下次加载该视图时,将创建并保留这三个视图的另一个副本。 但是,有一个简单而优雅的解决方案,即使用局部变量作为在弱变量中捕获视图的方法。

现在,对该视图的引用很少,并且没有保留周期。

难题#2-实例变量

这是有时会出现的另一种有问题的模式:

在上面的代码中有两个问题。 让我们解决第一个问题。 在flatMapLatest { self.createPostModel.createPost(text: $0, image: $1) }行中,没有显式self不会编译实例变量,这将与presenter和闭包创建一个保留周期。 我们可以使用另一个局部常量来解决此问题,以获取模型参考而不引用self。 看起来像这样:

难题#3-函数参考

上面的代码中仍然存在一个严重的问题,它将在视图和演示者之间创建一个保留周期。 你能发现吗? 它在subscribe(onNext: view.pop) 。 这是微妙的。 事实证明,如果在Swift中使用函数引用,则编译器会创建一个对要在其上执行函数的实例的强引用。 因此,即使view变量本身被声明为weak,使用view.pop仍会创建对view的强引用。 有趣的是,使用view.pop()将具有弱引用。 也许将来的Swift版本会解决这个问题。 在撰写本文时,Swift 4.2在使用函数引用语法时正在捕获强大的引用。 因此,使用此版本的代码可以避免上述保留周期:

建议:避免捕获列表

您会注意到,我避免将捕获列表与闭包一起使用。 恕我直言,捕获列表使代码混乱,我尽可能避免使用它们。 局部变量可以实现相同的结果,而不会使闭包混乱。 这是我从朋友rxswift.slack.com获得的指针,这是RxSwift开发阶段的必不可少的资源。 RxSwift松弛频道上有一个非常活跃的社区,可以帮助您补习教程和书籍。 如果尚未加入,请加入。

我的下一篇文章将提供一些使用Xcode内存树调试器来跟踪RxSwift内存泄漏的特定示例。 如果您喜欢这篇文章,请鼓掌。 要查看我的更多帖子,请关注我或访问我的页面。