有时,UserDefaults 会在重新启动应用程序后重新加载已删除的数据

Sometimes, UserDefaults reloads the deleted data after relaunching the app

我在对 save/load 数据使用 UserDefaults 时发现了一个奇怪的行为,我的操作如下。

  1. 滑动即可删除 tableView 中的 data/row。
  2. 重新启动应用程序以再次从 UserDefaults 加载数据。
  3. 我尝试了很多次并同时使用模拟器和 iPhone,有时当应用程序重新启动时删除的行会再次显示。

嗯,这是一个随机问题,我检查了我的代码但没有找到线索。我的数据模型非常简单,因此应该可以快速保存和加载。可以给我一些吗hint/advice?

型号

class Note: Codable {
    var content: String
    
    init(content: String) {
        self.content = content
    }
}

ViewController

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    ...
    var notes = [Note]()

    override func viewDidLoad() {
        super.viewDidLoad()
        ...
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        loadData()
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return notes.count
    }
    
   func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.accessoryType = .disclosureIndicator
        
        let node = notes[indexPath.row].content
        cell.textLabel?.text = node
        
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let vc = DetailViewController()
        vc.bodyText = notes[indexPath.row].content
        
        navigationController?.pushViewController(vc, animated: true)
    }
    
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            notes.remove(at: indexPath.row)
            print("notes: \(notes)")
            saveData()
            tableView.deleteRows(at: [indexPath], with: .fade)
            count = notes.count
        }
    }
    
    @objc func createNewNote() {
        let vc = NewNoteViewController()
        vc.notes = notes
        navigationController?.pushViewController(vc, animated: true)
    }
    
    func loadData() {
        DispatchQueue.global().async { [weak self] in
            let defaults = UserDefaults.standard

            if let savedNotes = defaults.object(forKey: "notes") as? Data {
                let jsonDecoder = JSONDecoder()
                do {
                    self?.notes = try jsonDecoder.decode([Note].self, from: savedNotes)
                    print("Load notes: \(String(describing: self?.notes))")
                    DispatchQueue.main.async {
                        self?.tableView.reloadData()
                        self?.count = self?.notes.count ?? 0
                    }
                } catch {
                    print("Failed to load notes")
                }
            }
        }
    }
    
    func saveData() {
        DispatchQueue.global().async { [weak self] in
            let jsonEncoder = JSONEncoder()
            
            if let savedData = try? jsonEncoder.encode(self?.notes) {
                let defaults = UserDefaults.standard
                defaults.set(savedData, forKey: "notes")
                print("value saved")
            } else {
                print("Failed to save notes")
            }
        }
    }
}

UserDefaults 不会立即将数据写入磁盘,出于性能原因,它会等待执行批量写入。

如果您通过 Xcode 重新启动应用程序,它可能会在应用程序有机会保留更改之前终止应用程序。

尝试强制关闭设备上的应用程序,而不是使用 Xcode 重新启动,或者再等几秒钟再重新启动,这不应该发生。

明确地说,您所看到的不是问题,这是您启动应用程序的方式的副作用。别担心,安装后它不会影响您的应用程序的工作方式。

有可能 [weak self] 在异步任务有机会 运行 之前被处理掉。

  1. 尝试将 self.notes 作为参数传递给 saveData(_ notes: [Note]) 而不是通过闭包内的 self?.notes 访问它。

  2. 这里可能有点过头了,但你可能想看看: Extending Your App's Background Execution Time