- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我决心完全理解为什么这不会导致引用循环。以及此处内存管理的每个阶段所发生的一般情况。
我有以下设置:
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
,它有一个捕获 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/
考虑以下代码: CurrentValueSubject(()) .eraseToAnyPublisher() .sink { compl
我是一名优秀的程序员,十分优秀!