Swift Inout如何在不更改时不复制属性,不触发对象设置器 [英] Swift inout how to not copy back property when not changed, to not trigger objects setters

查看:118
本文介绍了Swift Inout如何在不更改时不复制属性,不触发对象设置器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

您可以通过将inout关键字放在 其参数定义的开始.输入输出参数具有值 传递给函数,由函数修改,并且是 返回该函数以替换原始值.

You write an in-out parameter by placing the inout keyword at the start of its parameter definition. An in-out parameter has a value that is passed in to the function, is modified by the function, and is passed back out of the function to replace the original value.

但是如果根本不更改结果,如何不将其复制回去

But how to not copy back the result if it was not changed at all

我有一个数据库解析器 仅当其值更改时才分配attr,但是,采用inout的行为,始终设置传入的attr(将我的数据库对象标记为脏并已更改:/)

I have a database parser which assigns the attr only when it's value changes, however, with the behavior of inout, the attr that is passed in is always set (marking my database object dirty and changed :/ )

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)

是否可以进行修改,因此仅在传递的属性确实发生更改时才设置该值?

推荐答案

这是inout的工作方式.您无法更改. inout的字面意思是首先将值复制到函数中,最后将值复制到函数中".它不会做任何分析来确定是否在运行时触及了该值.

This is how inout works. You can't change that. inout literally means "copy the value into the function at the start and copy the value out of the function at the end." It doesn't do any analysis to decide whether the value was touched at runtime.

一种解决方案是检查观察者中的琐碎集,例如:

One solution is to check for trivial sets in the observer, for example:

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

作为另一种方法,我建议使用关键路径.假设数据库对象是引用类型(类),我相信以下将满足您的要求:

As another approach, I suggest keypaths. Assuming that the database object is a reference type (class), I believe the following will do what you want:

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
    }
}

由于您需要传递数据库本身,因此调用时间稍长一些:

The call is slightly longer because you need to pass the database itself:

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

可以通过将方法附加到数据库来使它更漂亮(尽管您仍然必须像传递self一样传递数据库):

That could be made a little prettier by attaching the method to the database (though you still have to pass the database, just as self):

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)


对于您关于使此泛型类型成为通用类型的问题,这非常简单(我刚刚添加了<Obj: AnyObject>并将对"db"的引用更改为"obj"):


To your question about making this generic over types, that's very straightforward (I just added <Obj: AnyObject> and changed the references to "db" to "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
    }
}

这篇关于Swift Inout如何在不更改时不复制属性,不触发对象设置器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆