Bra*_*ram 3 memory-leaks memory-management swift combine
我很难弄清楚如何创建一个包含 的 Swift 组合管道,其中.flatMap引用了self. 为了防止保留周期,这应该是一个[weak self]引用,但这不适用于.flatMap.
这是一个显示我的问题的简化示例:
import Foundation
import Combine
class SomeService {
func someOperation(label: String) -> Future<String, Never> {
Future { promise in
print("Starting work for", label)
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
print("Finished work for", label)
promise(.success(label))
}
}
}
}
class SomeDataSource {
let someService = SomeService()
var cancellables = Set<AnyCancellable>()
deinit {
print("Deinit SomeDataSource")
}
func complexOperation() {
// First part 'defined' is inside the complexOperation method:
someService.someOperation(label: "First part")
// Second part is 'defined' in another method (it is shared with other tasks)
.flatMap { _ in self.partOfComplexOperation(label: "Second part") } // <--- This creates a retain cycle
.sink { label in
print("Received value in sink", label)
}
.store(in: &cancellables)
}
func partOfComplexOperation(label: String) -> Future<String, Never> {
someService.someOperation(label: label)
}
}
var someDataSource: SomeDataSource? = SomeDataSource()
someDataSource?.complexOperation()
someDataSource = nil
Run Code Online (Sandbox Code Playgroud)
输出:
Starting work for First part
Finished work for First part
Starting work for Second part
Finished work for Second part
Received value in sink Second part
Deinit SomeDataSource
Run Code Online (Sandbox Code Playgroud)
这里的问题是我希望SomeDataSource在开始“第一部分”之后立即取消初始化,甚至不开始第二部分。所以我正在寻找的输出是:
Starting work for First part
Deinit SomeDataSource
Finished work for First part
Run Code Online (Sandbox Code Playgroud)
.flatMap我正在寻找和的某种组合.compactMap。这存在吗?如果我首先.compactMap { [weak self] _ in self }得到预期的结果,但也许有更好的方法?
Starting work for First part
Finished work for First part
Starting work for Second part
Finished work for Second part
Received value in sink Second part
Deinit SomeDataSource
Run Code Online (Sandbox Code Playgroud)
输出:
Starting work for First part
Deinit SomeDataSource
Finished work for First part
Run Code Online (Sandbox Code Playgroud)
这里的解决办法是不保留自我。你甚至不希望 self 出现在 flatMap 中,所以为什么要保留它......
let label = someService.someOperation(label: "First part")
.flatMap { [someService] _ in
someService.someOperation(label: label)
}
Run Code Online (Sandbox Code Playgroud)
当然,看到所有这些工作都在进行someService意味着该服务缺少某些功能。看到结果someOperation被忽略也可能是一个危险信号。
如果您确实处于必须使用 self 的情况,那么解决方案将如下所示:
let foo = someOperation()
.flatMap { [weak self] in
self?.otherOperation() ?? Empty(completeImmediately: true)
}
Run Code Online (Sandbox Code Playgroud)
或者你可能会考虑这样的事情:
someOperation()
.compactMap { [weak someService] _ in
someService?.otherOperation()
}
.switchToLatest()
Run Code Online (Sandbox Code Playgroud)
otherOperation()如果来自 的新事件将会取消someOperation()。