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))
    }
}