jeh*_*jeh 3 retain-cycle swiftui combine
我决心完全理解为什么这不会导致引用循环。一般来说,内存管理的每个阶段都发生了什么。
我有以下设置:
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()
}
}
Run Code Online (Sandbox Code Playgroud)
因此,当我创建 PresenterView 然后关闭它时,我会得到成功的 deinit 打印。
但是我不明白为什么它们在这里没有参考周期。DataListViewModel
hascancellables
其中有一个捕获 self 的订阅。所以DataListViewModel
-> 订阅和订阅 -> DataListViewModel
. 怎样才能deinit
触发呢?一般来说,有没有一个好的方法来理解在这种情况下是否存在保留周期?
正如您所期望的,该闭包确实保留了对 的强烈引用self
。关闭本身由Sink
订户维护。
如果没有发生其他情况,这就是内存泄漏,因为订阅者永远不会被取消,因为永远AnyCancellable
不会被释放,因为 self
永远不会取消初始化,并且self
永远不会取消初始化,因为订阅者持有它的引用。
但是,在您的情况下,发布者完成,这是订阅者释放其关闭的另一种方式。因此,self
仅在管道完成后才释放。
为了说明这一点,我们可以使用 aPassthroughSubject
显式发送完成:
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()
}
Run Code Online (Sandbox Code Playgroud)
因为self
被捕获,所以直到 2 秒后发送完成后才被释放:
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()
}
Run Code Online (Sandbox Code Playgroud)
如果您注释掉该行subject.send(completion: .finished)
,则不会有deinit
:
sync
async
async 2 sec
deinit
Run Code Online (Sandbox Code Playgroud)
如果在闭包中使用[weak self]
,管道将取消:
sync
async
async 2 sec
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1121 次 |
最近记录: |