Dart:我必须取消 Stream 订阅并关闭 StreamSinks 吗?
Posted
技术标签:
【中文标题】Dart:我必须取消 Stream 订阅并关闭 StreamSinks 吗?【英文标题】:Dart: Do I have to cancel Stream subscriptions and close StreamSinks? 【发布时间】:2018-09-27 11:59:02 【问题描述】:我知道当我不想再接收任何事件时,我必须取消 Stream
订阅。
即使在收到“完成”事件后,我也必须这样做吗?还是会出现内存泄漏?
传递给另一个 Stream 的 addStream
的 Stream 会发生什么情况?他们会自动取消吗?
StreamSink
方面的相同问题如果流已经完成,我是否必须关闭它们?
【问题讨论】:
【参考方案1】:简短回答:不,但你应该。 StreamSubscription
或 StreamSink
的合同中没有任何内容要求关闭资源,但是如果您不关闭它们,某些用例可能会导致内存泄漏,即使在某些情况下这样做也是如此可能会令人困惑。这些类的部分混淆在于它们被重载,并且处理两个相当不同的用例:
-
资源流(如文件 I/O、网络访问)
事件流(如点击处理程序)
让我们一次一个地解决这些问题,首先,StreamSubscription
:
StreamSubscription
当您将listen
发送到Stream
时,您会收到StreamSubscription
。一般来说,当您完成收听Stream
时,无论出于何种原因,您都应该关闭订阅。如果选择不这样做,并非所有流都会泄漏内存,但有些流会泄漏 - 例如,如果您正在从文件中读取输入,则不关闭流意味着文件的句柄可能保持打开状态。
所以,虽然不是严格要求,但我会总是在访问完流时cancel
。
StreamSink
StreamSink
最常见的实现是StreamController
,它是创建Stream
的编程接口。通常,当您的流完成(即所有数据已发出)时,您应该关闭控制器。
这里有点令人困惑。让我们看看这两种情况:
文件 I/O
假设您正在创建一个 API 来逐行异步读取文件:
Stream<String> readLines(String path);
要实现这一点,您可以使用StreamController
:
Stream<String> readLines(String path)
SomeFileResource someResource;
StreamController<String> controller;
controller = new StreamController<String>(
onListen: ()
someResource = new SomeFileResource(path);
// TODO: Implement adding to the controller.
,
);
return controller.stream;
在这种情况下,当读完最后一行时关闭controller
会很有意义。这会向用户发出信号(done 事件),表明文件已被读取,并且是有意义的(例如,您可以在那时关闭 File 资源)。
活动
假设您正在创建一个 API 来收听 HackerNews 上的新闻文章:
Stream<String> readHackerNews();
在这里,关闭底层接收器/控制器的意义不大。 HackerNews 会停止吗?像这样的事件流(或 UI 程序中的 click
处理程序)传统上不会在没有用户访问的情况下“停止”(即取消 StreamSubscription
)。
您可以在完成后关闭控制器,但这不是必需的。
希望这是有道理的,可以帮助你!
【讨论】:
谢谢!只有您的简短回答与您以后的阐述不一致。但它让我现在有了更好的理解。非常感谢 我认为答案确实是“是的,你应该”,而“不,你没有必须”在技术上是正确的(如详细说明由剩余的文本)。也就是说,在某些情况下您不需要这样做,但总是自己清理会更容易(并将例外情况留给少数情况下不太容易并且你知道你不需要需要)。当您在订阅中看到listen
与 cancel
匹配时,比查看代码却看不到任何对象生命周期的迹象更容易理解代码。
我认为 Dart 不允许重载。你这是什么意思?【参考方案2】:
我发现如果我有这样的代码:
Stream<String> readHackerNews(String path)
StreamController<String> controller = StreamController<String>();
......
return controller.stream;
我看到一条警告消息“关闭 'dart.core.Sink' 的实例”。在 Visual Studio 代码中。 为了解决这个警告,我添加了
controller.close()
到 OnCancel 事件的事件处理程序,见下文:
Stream<String> readHackerNews(String path)
StreamController<String> controller = StreamController<String>();
//TODO: your code here
controller.onCancel = ()
controller.close();
;
return controller.stream;
希望这会有所帮助!
【讨论】:
以上是关于Dart:我必须取消 Stream 订阅并关闭 StreamSinks 吗?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Navigator.of(context).push(....) 之后临时取消订阅 Stream?