SwiftUI Class 不会 DeInit
SwiftUI Class won't DeInit
我有一个 class class MapSearch,当我需要自动完成地址结果时,我会实例化它。它工作得很好,但它从不取消初始化,我不明白为什么。
通过创建以下文件轻松进行测试。导航到测试页面后使用后退按钮并查看控制台消息。您将看到视图模型按应有的方式初始化和取消初始化,但您只会看到 MapSearch 初始化。
HomeView.swift
import SwiftUI
struct HomeView: View {
var body: some View {
NavigationView {
NavigationLink(destination: TestView(viewModel: TestViewModel()) {
Text("TestView")
}
}
}
}
TestView.swift
import SwiftUI
struct TestView: View {
@StateObject var viewModel: ViewModel
var body: some View {
Text("Hello World")
}
}
TestViewModel.swift
import Foundation
extension TestView {
@MainActor
class ViewModel: ObservableObject {
@Published var mapSearch: MapSearch()
init() {
print("Test View Model Initialized")
}
deinit {
print("Test View Model Deinitialized")
}
}
}
MapSearch.swift
import Combine
import CoreLocation
import Foundation
import MapKit
/// Uses MapKit and CoreLocation to auto-complete an address
class MapSearch: NSObject, ObservableObject {
@Published var countryName: String = "United States"
@Published var locationResults: [MKLocalSearchCompletion] = []
@Published var searchTerm = ""
private var cancellables: Set<AnyCancellable> = []
private var searchCompleter = MKLocalSearchCompleter()
private var currentPromise: ((Result<[MKLocalSearchCompletion], Error>) -> Void)?
override init() {
super.init()
searchCompleter.delegate = self
searchCompleter.resultTypes = MKLocalSearchCompleter.ResultType([.address])
$searchTerm
.debounce(for: .seconds(0.2), scheduler: RunLoop.main)
.removeDuplicates()
.flatMap({ (currentSearchTerm) in
self.searchTermToResults(searchTerm: currentSearchTerm, countryName: self.countryName)
})
.sink(receiveCompletion: { (_) in
}, receiveValue: { (results) in
// Show country specific results
self.locationResults = results.filter { [=14=].subtitle.contains(self.countryName) }
})
.store(in: &cancellables)
print("MapSearch Initialized")
}
deinit {
print("MapSearch Deinitialized")
}
func searchTermToResults(searchTerm: String, countryName: String) -> Future<[MKLocalSearchCompletion], Error> {
Future { promise in
self.searchCompleter.queryFragment = searchTerm
self.currentPromise = promise
}
}
}
extension MapSearch: MKLocalSearchCompleterDelegate {
func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
currentPromise?(.success(completer.results))
}
func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
// deal with the error here, it will finish the Combine publisher stream
currentPromise?(.failure(error))
}
}
需要调整 MapSearch class 以在组合调用中添加 [weak self]。现在它正确地取消了。
参考代码如下:
import Combine
import CoreLocation
import Foundation
import MapKit
/// Uses MapKit and CoreLocation to auto-complete an address
class MapSearch: NSObject, ObservableObject {
@Published var countryName: String = "United States"
@Published var locationResults: [MKLocalSearchCompletion] = []
@Published var searchTerm = ""
private var cancellables: Set<AnyCancellable> = []
private var searchCompleter = MKLocalSearchCompleter()
private var currentPromise: ((Result<[MKLocalSearchCompletion], Error>) -> Void)?
override init() {
super.init()
searchCompleter.delegate = self
searchCompleter.resultTypes = MKLocalSearchCompleter.ResultType([.address])
$searchTerm
.debounce(for: .seconds(0.2), scheduler: RunLoop.main)
.removeDuplicates()
.flatMap({ [weak self] (currentSearchTerm) in
(self?.searchTermToResults(searchTerm: currentSearchTerm, countryName: self?.countryName ?? "")) ??
Future { [weak self] promise in
self?.searchCompleter.queryFragment = self?.searchTerm ?? ""
self?.currentPromise = promise
}
})
.sink(receiveCompletion: { (_) in
}, receiveValue: { [weak self] (results) in
// Show country specific results
self?.locationResults = results.filter { [=10=].subtitle.contains(self?.countryName ?? "") }
})
.store(in: &cancellables)
print("MapSearch Initialized")
}
deinit {
print("MapSearch Deinitialized")
}
func searchTermToResults(searchTerm: String, countryName: String) -> Future<[MKLocalSearchCompletion], Error> {
Future { [weak self] promise in
self?.searchCompleter.queryFragment = searchTerm
self?.currentPromise = promise
}
}
}
extension MapSearch: MKLocalSearchCompleterDelegate {
func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
currentPromise?(.success(completer.results))
}
func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
// deal with the error here, it will finish the Combine publisher stream
currentPromise?(.failure(error))
}
}
我有一个 class class MapSearch,当我需要自动完成地址结果时,我会实例化它。它工作得很好,但它从不取消初始化,我不明白为什么。
通过创建以下文件轻松进行测试。导航到测试页面后使用后退按钮并查看控制台消息。您将看到视图模型按应有的方式初始化和取消初始化,但您只会看到 MapSearch 初始化。
HomeView.swift
import SwiftUI
struct HomeView: View {
var body: some View {
NavigationView {
NavigationLink(destination: TestView(viewModel: TestViewModel()) {
Text("TestView")
}
}
}
}
TestView.swift
import SwiftUI
struct TestView: View {
@StateObject var viewModel: ViewModel
var body: some View {
Text("Hello World")
}
}
TestViewModel.swift
import Foundation
extension TestView {
@MainActor
class ViewModel: ObservableObject {
@Published var mapSearch: MapSearch()
init() {
print("Test View Model Initialized")
}
deinit {
print("Test View Model Deinitialized")
}
}
}
MapSearch.swift
import Combine
import CoreLocation
import Foundation
import MapKit
/// Uses MapKit and CoreLocation to auto-complete an address
class MapSearch: NSObject, ObservableObject {
@Published var countryName: String = "United States"
@Published var locationResults: [MKLocalSearchCompletion] = []
@Published var searchTerm = ""
private var cancellables: Set<AnyCancellable> = []
private var searchCompleter = MKLocalSearchCompleter()
private var currentPromise: ((Result<[MKLocalSearchCompletion], Error>) -> Void)?
override init() {
super.init()
searchCompleter.delegate = self
searchCompleter.resultTypes = MKLocalSearchCompleter.ResultType([.address])
$searchTerm
.debounce(for: .seconds(0.2), scheduler: RunLoop.main)
.removeDuplicates()
.flatMap({ (currentSearchTerm) in
self.searchTermToResults(searchTerm: currentSearchTerm, countryName: self.countryName)
})
.sink(receiveCompletion: { (_) in
}, receiveValue: { (results) in
// Show country specific results
self.locationResults = results.filter { [=14=].subtitle.contains(self.countryName) }
})
.store(in: &cancellables)
print("MapSearch Initialized")
}
deinit {
print("MapSearch Deinitialized")
}
func searchTermToResults(searchTerm: String, countryName: String) -> Future<[MKLocalSearchCompletion], Error> {
Future { promise in
self.searchCompleter.queryFragment = searchTerm
self.currentPromise = promise
}
}
}
extension MapSearch: MKLocalSearchCompleterDelegate {
func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
currentPromise?(.success(completer.results))
}
func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
// deal with the error here, it will finish the Combine publisher stream
currentPromise?(.failure(error))
}
}
需要调整 MapSearch class 以在组合调用中添加 [weak self]。现在它正确地取消了。
参考代码如下:
import Combine
import CoreLocation
import Foundation
import MapKit
/// Uses MapKit and CoreLocation to auto-complete an address
class MapSearch: NSObject, ObservableObject {
@Published var countryName: String = "United States"
@Published var locationResults: [MKLocalSearchCompletion] = []
@Published var searchTerm = ""
private var cancellables: Set<AnyCancellable> = []
private var searchCompleter = MKLocalSearchCompleter()
private var currentPromise: ((Result<[MKLocalSearchCompletion], Error>) -> Void)?
override init() {
super.init()
searchCompleter.delegate = self
searchCompleter.resultTypes = MKLocalSearchCompleter.ResultType([.address])
$searchTerm
.debounce(for: .seconds(0.2), scheduler: RunLoop.main)
.removeDuplicates()
.flatMap({ [weak self] (currentSearchTerm) in
(self?.searchTermToResults(searchTerm: currentSearchTerm, countryName: self?.countryName ?? "")) ??
Future { [weak self] promise in
self?.searchCompleter.queryFragment = self?.searchTerm ?? ""
self?.currentPromise = promise
}
})
.sink(receiveCompletion: { (_) in
}, receiveValue: { [weak self] (results) in
// Show country specific results
self?.locationResults = results.filter { [=10=].subtitle.contains(self?.countryName ?? "") }
})
.store(in: &cancellables)
print("MapSearch Initialized")
}
deinit {
print("MapSearch Deinitialized")
}
func searchTermToResults(searchTerm: String, countryName: String) -> Future<[MKLocalSearchCompletion], Error> {
Future { [weak self] promise in
self?.searchCompleter.queryFragment = searchTerm
self?.currentPromise = promise
}
}
}
extension MapSearch: MKLocalSearchCompleterDelegate {
func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
currentPromise?(.success(completer.results))
}
func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
// deal with the error here, it will finish the Combine publisher stream
currentPromise?(.failure(error))
}
}