Swift inout 如何在未更改时不复制回 属性,以不触发对象设置器
Swift inout how to not copy back property when not changed, to not trigger objects setters
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.
但是如果根本没有改变,如何不复制回结果
我有一个数据库解析器
它仅在 attr
的值发生变化时分配 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)
有没有办法修改,只有传入的属性真正改变时才设置值?
这就是 inout
的工作原理。你无法改变这一点。 inout
字面意思是 "copy the value into the function at the start and copy the value out of the function at the end." 它不做任何分析来决定该值是否在运行时被触及。
一种解决方案是检查观察者中的平凡集,例如:
var someAttr: String? {
didSet {
guard someAttr != oldValue else { return }
...
}
}
作为另一种方法,我建议使用键路径。假设数据库对象是引用类型(class),我相信下面会做你想要的:
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)
关于你关于在类型上创建泛型的问题,这非常简单(我刚刚添加了 <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
}
}
一个解决方案也是将集合移动到一个块中
之前
importStringAttribute(json, "someAttr", &myDBObject.someAttr)
之后
importStringAttribute(json, "someAttr", myDBObject.someAttr) { myDBObject.someAttr = [=11=]}
代码
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)
}
}
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.
但是如果根本没有改变,如何不复制回结果
我有一个数据库解析器
它仅在 attr
的值发生变化时分配 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)
有没有办法修改,只有传入的属性真正改变时才设置值?
这就是 inout
的工作原理。你无法改变这一点。 inout
字面意思是 "copy the value into the function at the start and copy the value out of the function at the end." 它不做任何分析来决定该值是否在运行时被触及。
一种解决方案是检查观察者中的平凡集,例如:
var someAttr: String? {
didSet {
guard someAttr != oldValue else { return }
...
}
}
作为另一种方法,我建议使用键路径。假设数据库对象是引用类型(class),我相信下面会做你想要的:
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)
关于你关于在类型上创建泛型的问题,这非常简单(我刚刚添加了 <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
}
}
一个解决方案也是将集合移动到一个块中
之前
importStringAttribute(json, "someAttr", &myDBObject.someAttr)
之后
importStringAttribute(json, "someAttr", myDBObject.someAttr) { myDBObject.someAttr = [=11=]}
代码
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)
}
}