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
相关产品推荐
相关产品推荐

