我决心完全理解为什么这没有引起参考周期。通常,这里的内存管理每个阶段都会发生什么情况。
我有以下设置:
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打印。
但是我不明白为什么它们在这里没有参考周期。 DataListViewModel
具有cancellables
,该订阅具有捕获自身的订阅。因此,DataListViewModel
->订阅和订阅-> DataListViewModel
。 deinit
如何被触发?总的来说,有一种很好的方法来了解这种情况下是否存在保留周期?
答案 0 :(得分:1)
如您所料,闭包确实保留了对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