gpt4 book ai didi

ios - 如何使用Combine的AsyncPublisher解决内存泄漏

转载 作者:行者123 更新时间:2023-12-03 07:56:54 26 4
gpt4 key购买 nike

异步迭代 AsyncPublisher(某种异步序列)时出现内存泄漏问题!

在下面的代码中,我有timerSequence ( AsyncPublisher<Publishers.Autoconnect<Timer.TimerPublisher>> ),并且在 inti 上我异步迭代该计时器序列。但 [weak self] 之后的事件捕获内部Task它仍然没有解除分配!不知道这是联合收割机的错误!!

GitHub 演示:https://github.com/satishVekariya/AnyPublisherMemoryLeakDemoApp

import Foundation
import Combine

class MyServiceClass {

let timerSequence = Timer.publish(every: 1, on: .main, in: .default).autoconnect().values

init() {
Task { [weak self] in
await self?.setUpStreamIteration()
}
}

func setUpStreamIteration() async {
for await time in timerSequence {
print(time)
}
}
}

var service: MyServiceClass? = MyServiceClass()
service = nil

输出:

2023-03-26 00:14:11 +0000
2023-03-26 00:14:12 +0000
2023-03-26 00:14:13 +0000
2023-03-26 00:14:14 +0000
2023-03-26 00:14:15 +0000
2023-03-26 00:14:16 +0000
2023-03-26 00:14:17 +0000
2023-03-26 00:14:18 +0000
2023-03-26 00:14:19 +0000
...

最佳答案

首先,我建议我们简化示例:

class MyServiceClass {
private let timerSequence = Timer
.publish(every: 1, on: .main, in: .default)
.autoconnect()
.values

func setUpStreamIteration() async {
for await time in timerSequence {
print(time)
}
}
}

// It obviously doesn’t make much sense to create a local var for a service
// and then let it immediately fall out of scope, but we’re doing this just
// to illustrate the problem…

func foo() {
let service = MyServiceClass()
Task { await service.setUpStreamIteration() }
}

一旦setUpStreamIteration开始运行,MyServiceClass就无法释放,直到setUpStreamIteration完成。但如果没有什么东西来停止这个异步序列,这个方法将永远不会完成。一旦 setUpStreamIteration 启动,在 Task {…} 中插入 weak 捕获列表将无法拯救您。 (它实际上引入了服务的释放和此方法的启动之间的竞争,这使我们重现/诊断此问题的尝试变得复杂。)

如果您使用 SwiftUI,一种方法是在 .task View 修饰符中创建流,当 View 关闭时它将自动取消它。 (请注意,要实现这一点,必须保持结构化并发并避免 Task { … } 非结构化并发。)

另一个典型的解决方案是显式选择非结构化并发,在启动序列时保存任务,并添加一个方法来停止序列。例如:

class MyServiceClass {
private var task: Task<Void, Never>?

private let timerSequence = Timer
.publish(every: 1, on: .main, in: .default)
.autoconnect()
.values

func setUpStreamIteration() {
task = Task {
for await time in timerSequence {
print(time)
}
}
}

func stopStreamIteration() {
task?.cancel()
}
}

您只需要确保在释放它之前调用 stopStreamIteration (例如,当关联的 View 消失时)。


顺便说一句,如果你想避免引入Combine,可以使用 AsyncTimerSequence来自Swift Async Algorithms包。

for await time in AsyncTimerSequence(interval: .seconds(1), clock: .continuous) {
print(time)
}

您仍然遇到需要取消此序列的相同问题(使用上面概述的两种方法之一),但至少它避免了在混合中引入组合)。您还可以编写自己的 AsyncStream 包装 Timer 或 GCD 计时器,但没有理由重新发明轮子。

关于ios - 如何使用Combine的AsyncPublisher解决内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75845261/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com