我的应用程序包含各种小部件,用于指示实时值(温度、CPU 使用率、内存等)。这些小部件通过单个 websocket 收到的 JSON 消息进行更新。
目前,更新是实时处理的,因此一旦收到新值,我就会更新我的小部件。
讯息如下:{widget: "widgetId", value: 150}
。
我的问题是,有时同一个小部件每秒会进行多次更新,这会导致屏幕闪烁,并产生无用的性能成本。我想通过延迟 UI 更新以仅在一段时间内使用最新收到的值进行更新来减少这种情况。
我想探讨以下两个选择:
每 X 秒同时更新所有小部件一次(通过仅保留每个小部件的最新更新并将它们一次全部发送到 UI)。
每 X 秒最多更新一次小部件(通过通道去抖动)。
我的套接字处理程序如下所示:
socket.receive(completionHandler: { [weak self] result in
switch result {
case .success(let message):
switch message {
case .data(let data):
guard let update = try? WidgetUpdate(serializedData: data) else {
print("Failed to deserialize data")
return
}
DispatchQueue.main.async {
self?.updateWidget(update: update)
}
@unknown default:
print("unknown")
}
}
}
)
到目前为止我尝试的是使用PassthroughSubject
这样的方法:
let subject = PassthroughSubject<WidgetUpdate, Never>()
cancellable = subject
.debounce(for: .seconds(0.5), scheduler: RunLoop.main)
.sink { update in
self?.updateWidget(update: update)
}
// And to publish inside the publisher from the socket handler like this
subject.send(update)
问题是它显然会消除“所有”更新的抖动,而不是通过小部件。
我想避免通过小部件创建一个 PassthroughSubject,因为可以动态添加或删除小部件,并且我不想管理相关发布者的创建/删除。
我认为 ReactiveX GroupBy操作符会对你有所帮助。不幸的是,Combine 没有自带的 GroupBy,但可以通过扫描来实现:
一旦你有了这个,我相信类似这样的事情应该可以解决你的问题(这是基于你原来的例子):
以下是根据您的实际 WebSocket 问题得出的一些结论:
此外,在我看来,您真正想要的是节流,而不是防抖动,尽管对于您的用例,大多数用户可能不会注意到两者之间的区别。只要您将发布者分组,您就可以选择节流或防抖动。