【问题标题】:Swift detect in the setter which object in an array was changedSwift 在 setter 中检测数组中的哪个对象被更改
【发布时间】:2021-02-06 17:43:00
【问题描述】:

我有一个 Habit 对象数组,我正在内存中对其进行修改,还有一个 getter/setter 用于在进行更改时将其作为 JSON 读取和写入到文档存储中。我已将每个对象拆分为自己的 JSON 文件,到目前为止一切正常,但是更改数组中的一个对象将重写所有文件。我知道这是我指示我的 setter 执行的操作,但是有没有办法只在 newValue 中写入已更改的对象?

extension Storage {
    static var habits: [Habit] {
        get { ... }
        set {
            newValue.forEach({
                let data = try! JSONEncoder().encode($0)
                do { try data.write(to: Storage.habitsFolder.appendingPathComponent("habit-\($0.id).json")) }
                catch let error {
                    print("Failed to write Habit \($0.id): \(error.localizedDescription)")
                }
            })
        }
    }
}

我现在做出改变的方式是Storage.habits[0].name = "Some name"。它调用设置器,然后为每个习惯重写文件。所以我想知道是否有某种方法可以检测 newValue 的哪一部分发生了变化或将索引传递给它并只更新该文件。

唯一的方法是让数组是 get-only 并使用不同的方法来设置每个习惯文件吗?

谢谢,如果这是一个愚蠢的问题,我们深表歉意。

更新:添加习惯类以获取更多上下文

class Habit: Codable, CustomStringConvertible {
    // MARK: - Properties
    var id: Int
    var name: String?
    var notes: String?
    var icon: String
    var repeatPattern: RepeatPattern
    var entries: [HabitEntry]
    var reminders: [Reminder]
    init(id: Int, name: String?, notes: String?, icon: String, entries: [HabitEntry], reminders: [Reminder]) {
        self.id = id
        self.name = name
        self.notes = notes
        self.icon = icon
        self.repeatPattern = RepeatPattern(pattern: 0, startDay: calendar.today, enabledWeekdays: [1,2,3,4,5,6,7], expectedTimes: 1)
        self.entries = entries
        self.reminders = reminders
    }
    var description: String { return "Habit id: \(id), named: \(name ?? "nil"), \nnotes: \(notes ?? "nil"), \nicon: \(icon), \nrepeatPattern: \(repeatPattern), \nentries: \(entries), \nreminders: \(String(describing: reminders))" }
}

【问题讨论】:

  • 将写入磁盘的json数据移到Habit对象中不是更容易吗?
  • 如果你从你的收藏中删除一个习惯,它将继续存在于磁盘上
  • 你也应该这样做来改变它
  • 将您的习惯对象声明添加到您的问题中
  • 有一些方法可以监控文件夹以检测它何时发生变化。也许您可以简单地监控习惯文件夹何时更改并更新您的数组。

标签: ios arrays swift getter-setter


【解决方案1】:

你可以先创建一个属性来存储现有的habits

class Storage {
    static var existingHabits = [Habit]()
}

然后,在set 中,查看哪些Habits 是新的:

static var habits: [Habit] {
    get { ... }
    set {

        var habitsToChange = [Habit]()

        if existingHabits.count == 0 { /// it's empty, just write with newValue
            habitsToChange = newValue
        } else {
            let differentIndicies = zip(existingHabits, newValue).enumerated().filter() {
                $1.0 != $1.1
            }.map{$0.0} /// from https://stackoverflow.com/a/30685226/14351818
        
            for index in differentIndicies {
                habitsToChange.append(newValue[index])
            }
        }

        habitsToChange.forEach({ /// loop over the habits that have changed
            do {
                try JSONEncoder().encode($0).write(to: habitsFolder.appendingPathComponent("habit-\($0.id).json"))
            } catch {
                print("Failed to write Habit \($0.id): \(error)")
            }
        })

        existingHabits = newValue /// set existing to the new value
    }
}

【讨论】:

  • 为什么要强制尝试编码? try JSONEncoder().encode($0).write....
  • btw catch let error 是多余的。只需catch 并摆脱localizedDescription
  • 其他一些您无法从静态属性访问实例属性的注意事项
  • 好吧,我来解决
  • Storage.existingHabits 是多余的,现在只需 existingHabits 就足够了。同样适用于habitsFolder
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-05-20
  • 1970-01-01
  • 1970-01-01
  • 2018-09-20
  • 1970-01-01
  • 2023-04-11
  • 1970-01-01
相关资源
最近更新 更多