You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift:如何在应用生命周期内保留TableView数据?

优化Firestore数据加载避免重复请求的方案

针对你每次进入视图控制器都调用getAllDevices查询Firestore的问题,这里有几个实用方案可以避免重复数据库操作,同时保证TableView数据的可用性:

1. 全局单例缓存数据

创建一个单例类专门管理设备数据,第一次请求后将数据缓存起来,后续进入视图控制器时直接复用缓存数据,仅在需要更新时重新请求。

实现步骤:

首先定义单例类:

class DeviceDataManager {
    // 单例实例
    static let shared = DeviceDataManager()
    
    // 缓存的数据
    private(set) var devicesMap: [String: [String]] = [:]
    private(set) var deviceNames: [String] = []
    
    // 更新缓存数据
    func updateData(map: [String: [String]], names: [String]) {
        devicesMap = map
        deviceNames = names
    }
    
    // 清除缓存(比如用户退出登录时调用)
    func clearData() {
        devicesMap.removeAll()
        deviceNames.removeAll()
    }
}

然后修改视图控制器的viewDidAppear方法:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    firebaseSource.firebaseDelegate = self
    if !isUserSignedIn() {
        navigateToLogin()
    } else {
        let dataManager = DeviceDataManager.shared
        // 优先使用缓存数据
        if !dataManager.deviceNames.isEmpty {
            self.devicesMap = dataManager.devicesMap
            self.deviceNames = dataManager.deviceNames
            self.tableView.reloadData()
        } else {
            // 缓存为空时才请求数据库
            repository?.getAllDevices(email: auth.currentUser?.email ?? "", completion: { [weak self] map, names in
                guard let self = self else { return }
                self.devicesMap = map
                self.deviceNames = names
                // 更新缓存
                dataManager.updateData(map: map, names: names)
                self.tableView.reloadData()
            })
        }
    }
    self.registerTableViewCells()
    self.tableView.delegate = self
    self.tableView.dataSource = self
}

如果需要主动刷新数据(比如下拉刷新),可以单独触发请求并更新缓存:

@objc func handleRefresh() {
    guard let email = auth.currentUser?.email else { return }
    repository?.getAllDevices(email: email, completion: { [weak self] map, names in
        guard let self = self else { return }
        self.devicesMap = map
        self.deviceNames = names
        DeviceDataManager.shared.updateData(map: map, names: names)
        self.tableView.reloadData()
        self.refreshControl?.endRefreshing()
    })
}

2. 监听Firestore实时更新(替代全量请求)

如果设备数据可能随时变化,可以使用Firestore的snapshotListener监听集合变化,这样只会在数据有更新时触发回调,无需每次进入视图控制器都全量请求。

实现步骤:

在视图控制器的viewDidLoad中添加监听:

override func viewDidLoad() {
    super.viewDidLoad()
    // ...其他初始化代码
    if isUserSignedIn() {
        guard let email = auth.currentUser?.email else { return }
        // 添加实时监听
        repository?.addDevicesListener(email: email) { [weak self] map, names in
            guard let self = self else { return }
            self.devicesMap = map
            self.deviceNames = names
            self.tableView.reloadData()
            // 同时更新单例缓存
            DeviceDataManager.shared.updateData(map: map, names: names)
        }
    }
}

在viewWillDisappear中移除监听,避免内存泄漏:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    repository?.removeDevicesListener()
}

需要在你的repository类中实现监听相关方法:

class DeviceRepository {
    private var listenerRegistration: ListenerRegistration?
    
    func addDevicesListener(email: String, completion: @escaping ([String: [String]], [String]) -> Void) {
        // 替换为你的Firestore集合路径
        let collectionRef = Firestore.firestore().collection("users").document(email).collection("devices")
        listenerRegistration = collectionRef.addSnapshotListener { snapshot, error in
            guard let snapshot = snapshot, error == nil else {
                print("监听失败:\(error?.localizedDescription ?? "未知错误")")
                return
            }
            // 解析snapshot为你需要的map和names,这里仅为示例
            var map: [String: [String]] = [:]
            var names: [String] = []
            for document in snapshot.documents {
                let data = document.data()
                // 根据你的数据结构解析
                if let name = data["name"] as? String, let details = data["details"] as? [String] {
                    map[document.documentID] = details
                    names.append(name)
                }
            }
            completion(map, names)
        }
    }
    
    func removeDevicesListener() {
        listenerRegistration?.remove()
    }
}

3. 本地持久化缓存(支持离线使用)

如果需要支持离线场景,可以将数据保存到UserDefaults或Core Data中,进入视图控制器时先加载本地数据,后台静默请求最新数据并更新。

示例(使用UserDefaults):

添加本地缓存的工具方法:

extension YourViewController {
    // 保存数据到本地
    func saveDataToLocal(map: [String: [String]], names: [String]) {
        do {
            let mapData = try JSONSerialization.data(withJSONObject: map, options: [])
            UserDefaults.standard.set(mapData, forKey: "devicesMap")
            UserDefaults.standard.set(names, forKey: "deviceNames")
            UserDefaults.standard.synchronize()
        } catch {
            print("保存本地缓存失败:\(error)")
        }
    }
    
    // 从本地加载数据
    func loadDataFromLocal() -> ([String: [String]], [String])? {
        guard let mapData = UserDefaults.standard.data(forKey: "devicesMap"),
              let map = try? JSONSerialization.jsonObject(with: mapData, options: []) as? [String: [String]],
              let names = UserDefaults.standard.array(forKey: "deviceNames") as? [String] else {
            return nil
        }
        return (map, names)
    }
}

修改viewDidAppear方法:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    firebaseSource.firebaseDelegate = self
    if !isUserSignedIn() {
        navigateToLogin()
    } else {
        // 先加载本地缓存
        if let (localMap, localNames) = loadDataFromLocal() {
            self.devicesMap = localMap
            self.deviceNames = localNames
            self.tableView.reloadData()
            // 后台请求最新数据并更新
            guard let email = auth.currentUser?.email else { return }
            repository?.getAllDevices(email: email, completion: { [weak self] map, names in
                guard let self = self else { return }
                self.devicesMap = map
                self.deviceNames = names
                self.saveDataToLocal(map: map, names: names)
                self.tableView.reloadData()
            })
        } else {
            // 本地无缓存时请求数据库
            guard let email = auth.currentUser?.email else { return }
            repository?.getAllDevices(email: email, completion: { [weak self] map, names in
                guard let self = self else { return }
                self.devicesMap = map
                self.deviceNames = names
                self.saveDataToLocal(map: map, names: names)
                self.tableView.reloadData()
            })
        }
    }
    self.registerTableViewCells()
    self.tableView.delegate = self
    self.tableView.dataSource = self
}

方案选择建议

  • 如果数据更新频率低,全局单例缓存是最简单高效的方案;
  • 如果需要实时同步数据变化,Firestore实时监听是最优选择;
  • 如果需要支持离线使用,本地持久化缓存配合后台更新能提升用户体验。

内容的提问来源于stack exchange,提问作者Dylon Jaynes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 04:15:20