Быстро введите, как не копировать свойство back, если оно не изменено, чтобы не вызывать установщики объектов - PullRequest
1 голос
/ 27 марта 2020

в соответствии с документами

Вы вводите параметр in-out, помещая ключевое слово inout в начале определения его параметра. Параметр in-out имеет значение, которое передается в функцию, модифицируется функцией и передается обратно из функции для замены исходного значения.

Но как не копировать вернуть результат, если он вообще не был изменен

У меня есть анализатор базы данных, который назначает attr только при изменении его значения, однако при поведении inout передается attr, который всегда установлен (помечая мой объект базы данных как грязный и измененный: /)

func importStringAttribute(_ json: JSON, _ key: String, _ attr: inout String?) {
    if !json[key].exists() {
        return
    }
    if let v = json[key].string, v != attr {
        attr = v
    }
}

// the myDBObject.someAttr is always set 
importStringAttribute(json, "someAttr", &myDBObject.someAttr)

есть ли способ модификации, поэтому значение устанавливается только тогда, когда переданный атрибут действительно изменяется?

Ответы [ 2 ]

1 голос
/ 27 марта 2020

Одним из решений было бы также переместить набор в блок

До

importStringAttribute(json, "someAttr", &myDBObject.someAttr)

После

importStringAttribute(json, "someAttr", myDBObject.someAttr) { myDBObject.someAttr = $0}

код

func importStringAttribute(_ json: JSON, _ key: String, _ attr: String?, set:(_ value: String?)->()) {
    if !json[key].exists() {
        return
    }
    if let v = json[key].string, v != attr {
        set(v)
    }
}
1 голос
/ 27 марта 2020

Так работает inout. Вы не можете это изменить. inout буквально означает «скопировать значение в функцию в начале и скопировать значение из функции в конце». Он не выполняет никакого анализа, чтобы определить, было ли значение затронуто во время выполнения.

Одним из решений является проверка тривиальных наборов в наблюдателе, например:

var someAttr: String? {
    didSet {
        guard someAttr != oldValue else { return }
        ...
    }
}

В качестве другого подхода Предлагаю ключи. Предполагая, что объект базы данных является ссылочным типом (классом), я полагаю, что следующее будет делать то, что вы хотите:

func importStringAttribute(_ json: JSON, _ key: String, db: Database,
                           attr: ReferenceWritableKeyPath<Database, String?>) {
    if !json[key].exists() {
        return
    }
    if let v = json[key].string, v != db[keyPath: attr] {
        db[keyPath: attr] = v
    }
}

Вызов немного длиннее, потому что вам нужно передать саму базу данных:

importStringAttribute(json, "someAttr", db: myDBObject, attr: \.someAttr)

Это можно сделать немного красивее, если присоединить метод к базе данных (хотя вам все равно придется передавать базу данных, как и себя):

extension Database {
    func importStringAttribute(_ json: JSON, _ key: String,
                               _ attr: ReferenceWritableKeyPath<Database, String?>) {
        if !json[key].exists() {
            return
        }
        if let v = json[key].string, v != self[keyPath: attr] {
            self[keyPath: attr] = v
        }
    }

}

myDBObject.importStringAttribute(json, "someAttr", \.someAttr)

К вашему вопросу о сделать это generi c над типами, это очень просто (я просто добавил <Obj: AnyObject> и изменил ссылки на "db" на "obj"):

func importStringAttribute<Obj: AnyObject>(_ json: JSON, _ key: String, obj: Obj,
                           attr: ReferenceWritableKeyPath<Obj, String?>) {
    if !json[key].exists() {
        return
    }
    if let v = json[key].string, v != obj[keyPath: attr] {
        obj[keyPath: attr] = v
    }
}
...