#swift #memory-management #memory-leaks #combine
Вопрос:
Мне трудно понять, как создать конвейер быстрого объединения, содержащий .flatMap
ссылку на который self
. Чтобы предотвратить цикл сохранения, это должно быть [weak self]
ссылкой, но это не работает с a .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: amp;cancellables)
}
func partOfComplexOperation(label: String) -> Future<String, Never> {
someService.someOperation(label: label)
}
}
var someDataSource: SomeDataSource? = SomeDataSource()
someDataSource?.complexOperation()
someDataSource = nil
Выход:
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
Проблема здесь в том, что я хочу SomeDataSource
, чтобы мой был деинициализирован сразу после запуска «Первой части» и даже не начинал вторую часть. Итак, результат, который я ищу, это:
Starting work for First part
Deinit SomeDataSource
Finished work for First part
Я ищу какое — то сочетание .flatMap
и .compactMap
. Существует ли это? Если я сначала .compactMap { [weak self] _ in self }
получу ожидаемый результат, но, может быть, есть лучший способ?
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")
.compactMap { [weak self] _ in self }
// Second part is 'defined' in another method (it is shared with other tasks)
.flatMap { dataSource in dataSource.partOfComplexOperation(label: "Second part") }
.sink { label in
print("Received value in sink", label)
}
.store(in: amp;cancellables)
}
func partOfComplexOperation(label: String) -> Future<String, Never> {
someService.someOperation(label: label)
}
}
var someDataSource: SomeDataSource? = SomeDataSource()
someDataSource?.complexOperation()
someDataSource = nil
Выход:
Starting work for First part
Deinit SomeDataSource
Finished work for First part
Ответ №1:
Решение здесь состоит в том, чтобы не сохранять себя. Вы даже не хотите видеть себя на плоской карте, так зачем ее сохранять…
let label = someService.someOperation(label: "First part")
.flatMap { [someService] _ in
someService.someOperation(label: label)
}
Конечно, просмотр всей этой работы someService
подразумевает, что в сервисе отсутствует какая-то функциональность. Видя, что результат someOperation
игнорируется, это также может быть красным флагом.
Если бы вы действительно оказались в ситуации, когда вам пришлось использовать себя, то решение выглядело бы так:
let foo = someOperation()
.flatMap { [weak self] in
self?.otherOperation() ?? Empty(completeImmediately: true)
}
Или вы могли бы подумать о чем-то вроде:
someOperation()
.compactMap { [weak someService] _ in
someService?.otherOperation()
}
.switchToLatest()
Который будет отменен otherOperation()
, если поступит новое событие от someOperation()
.