gpt4 book ai didi

swiftui - 为什么receiveValue block Combine订阅没有retain cycle

转载 作者:行者123 更新时间:2023-12-01 23:45:08 25 4
gpt4 key购买 nike

我决心完全理解为什么这不会导致引用循环。以及此处内存管理的每个阶段所发生的一般情况。

我有以下设置:

struct PresenterView: View {
@State private var isPresented = false
var body: some View {
Text("Show")
.sheet(isPresented: $isPresented) {
DataList()
}
.onTapGesture {
isPresented = true
}
}
}

struct DataList: View {

@StateObject private var viewModel = DataListViewModel()

var body: some View {
NavigationView {
List(viewModel.itemViewModels, id: \.self) { itemViewModel in
Text(itemViewModel.displayText)
}.onAppear {
viewModel.fetchData()
}.navigationBarTitle("Items")
}
}
}

class DataListViewModel: ObservableObject {

private let webService = WebService()

@Published var itemViewModels = [ItemViewModel]()

private var cancellable: AnyCancellable?

func fetchData() {
cancellable = webService.fetchData().sink(receiveCompletion: { _ in
//...
}, receiveValue: { dataContainer in
self.itemViewModels = dataContainer.data.items.map { ItemViewModel($0) }
})
}

deinit {
print("deinit")
}

}

final class WebService {

var components: URLComponents {
//...
return components
}

func fetchData() -> AnyPublisher<DataContainer, Error> {
return URLSession.shared.dataTaskPublisher(for: components.url!)
.map { $0.data }
.decode(type: DataContainer.self, decoder: JSONDecoder())
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
}

因此,当我创建一个 PresenterView 然后关闭它时,我得到了一个成功的 deinit 打印。

但是我不明白为什么他们在这里没有引用循环。 DataListViewModelcancellables,它有一个捕获 self 的订阅。所以DataListViewModel -> 订阅和订阅 -> DataListViewModel。如何触发deinit?一般来说,是否有一种好的方法来了解在这种情况下是否存在保留周期?

最佳答案

如您所料,闭包确实保留了对 self 的强引用。闭包本身由 Sink 订阅者维护。

如果没有其他事情发生,这是一个内存泄漏,因为订阅者永远不会被取消,因为 AnyCancellable 永远不会被释放,因为 self 永远不会取消初始化,并且 self 永远不会取消初始化,因为订阅者持有它的引用。

但是,在您的情况下,发布者完成,这是订阅者发布其闭包的另一种方式。因此,self 仅在管道完成后释放。

为了说明,我们可以使用 PassthroughSubject 显式发送完成:

class Foo {
var c: AnyCancellable? = nil

func fetch() {
let subject = PassthroughSubject<String, Never>()

c = subject.sink {
self.c // capture self
print($0)
}

subject.send("sync")

DispatchQueue.main.async { subject.send("async") }

DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
subject.send("async 2 sec")
subject.send(completion: .finished)
}
}

deinit { print("deinit") }
}


do {
Foo().fetch()
}

因为 self 被捕获,所以直到 2 秒后发送完成后它才会被释放:

sync
async
async 2 sec
deinit

如果注释掉 subject.send(completion: .finished) 行,将不会有 deinit:

sync
async
async 2 sec

如果在闭包中使用[weak self],管道将取消:

sync
deinit

关于swiftui - 为什么receiveValue block Combine订阅没有retain cycle,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64316975/

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