gpt4 book ai didi

swift - 使用 keyPath 绑定(bind) 2 个属性(观察)

转载 作者:可可西里 更新时间:2023-11-01 02:03:28 28 4
gpt4 key购买 nike

我正在尝试创建一个例程来简化将一个属性绑定(bind)到另一个属性的过程,这是一种非常常见的操作。我在 Swift 4 和 XCode 9 中使用基于 block 的 KVO。

我希望能够编写以下内容以使用相应的 keyPath 绑定(bind)两个变量:

self.bind(to: \BindMe.myFirstName, from: \BindMe.person.firstName )

这是一个简化的示例,它生成了我无法解决的各种编译错误。可能是将 keyPath 错误地传递给了 func bind ,但是 setValue使用 keyPath 也无法编译。请查看代码中的注释以了解我遇到的编译错误。

class Person : NSObject
{
init( firstName:String, lastName:String )
{
self.firstName = firstName
self.lastName = lastName
}

@objc dynamic var firstName:String
@objc dynamic var lastName:String
}

class BindMe : NSObject
{
var observers = [NSKeyValueObservation]()
let person:Person

var myFirstName:String = "<no first name>"
var myLastName:String = "<no last name>"

init( person:Person )
{
self.person = person
self.setupBindings()
}

func setupBindings()
{
self.bind(to: \BindMe.myFirstName, from: \BindMe.person.firstName )
self.bind(to: \BindMe.myLastName, from: \BindMe.person.lastName )
}

// this func declaration is likely incorrect
func bind<T,Value,Value2>( to targetKeyPath:KeyPath<T,Value>, from sourceKeyPath:KeyPath<T,Value2>)
{
// Error: Generic parameter 'Value' could not be inferred
self.observers.append( self.observe(sourceKeyPath, options: [.initial,.new], changeHandler: { (object, change) in

// Error: Cannot convert value of type 'KeyPath<T, Value>' to expected argument type 'String'
self.setValue(change.newValue, forKeyPath: targetKeyPath)
}))
}
}

编辑

answer below帮助解决最初的编译问题。但是,为了使它真正有用,我需要能够将管道推送到父类(super class)中,如此处所示。这将使使用它的类变得非常简单,但我仍在为编译错误而苦苦挣扎:

Cannot invoke 'bind' with an argument list of type '(to: WritableKeyPath<PersonWatcher, PersonWatcher>, from: WritableKeyPath<PersonWatcher, PersonWatcher>)'

如果我将通用类型 T 传递给绑定(bind)例程,则会收到此错误:

Type 'BindBase' has no subscript members

class BindBase :NSObject
{
var observers = [NSKeyValueObservation]()

func bind<Value>(to targetKeyPath: ReferenceWritableKeyPath<BindBase, Value>, from sourceKeyPath: KeyPath<BindBase, Value>)
{
self.observers.append(self.observe(sourceKeyPath, options: [.initial, .new], changeHandler: { (object, change) in
self[keyPath: targetKeyPath] = change.newValue!
}))
}
}

class PersonWatcher : BindBase
{
@objc dynamic var person: Person

@objc var myFirstName: String = "<no first name>"
@objc var myLastName: String = "<no last name>"

init(person: Person) {
self.person = person
super.init()

self.bind(to: \PersonWatcher.myFirstName, from: \PersonWatcher.person.firstName)
self.bind(to: \PersonWatcher.myLastName, from: \PersonWatcher.person.lastName)
}
}

最佳答案

根据已接受的提案 SE-0161 Smart KeyPaths: Better Key-Value Coding for Swift ,您需要使用 ReferenceWritableKeyPath 使用下标将值写入具有引用语义的对象的键路径。

(您需要将经典的基于String 的键路径传递给setValue(_:forKeyPath:),而不是KeyPath。)

还有一些:

  • ValueValue2 需要相同才能赋值
  • T需要代表self
  • 的类型
  • KVC/KVO 目标属性需要是@objc
  • BindMe.init(person:) 需要 super.init()

所以,您的 BindMe 应该是这样的:

class BindMe: NSObject {
var observers = [NSKeyValueObservation]()
@objc let person: Person

@objc var myFirstName: String = "<no first name>"
@objc var myLastName: String = "<no last name>"

init(person: Person) {
self.person = person
super.init()
self.setupBindings()
}

func setupBindings() {
self.bind(to: \BindMe.myFirstName, from: \BindMe.person.firstName)
self.bind(to: \BindMe.myLastName, from: \BindMe.person.lastName)
}

func bind<Value>(to targetKeyPath: ReferenceWritableKeyPath<BindMe, Value>, from sourceKeyPath: KeyPath<BindMe, Value>) {
self.observers.append(self.observe(sourceKeyPath, options: [.initial, .new], changeHandler: { (object, change) in
self[keyPath: targetKeyPath] = change.newValue!
}))
}
}

对于编辑:

要求做一个BindBase之类的东西似乎很合理,所以我做了一些尝试。

实现

  • T需要代表self
  • 的类型

(where T == KeyPath.Root),使用Self 是最直观的,但不幸的是,它的使用在当前版本的 Swift 中仍然受到很大限制.

您可以使用 Selfbind 的定义移动到协议(protocol)扩展中:

class BindBase: NSObject, Bindable {
var observers = [NSKeyValueObservation]()
}

protocol Bindable: class {
var observers: [NSKeyValueObservation] {get set}
}

extension Bindable {
func bind<Value>(to targetKeyPath: ReferenceWritableKeyPath<Self, Value>, from sourceKeyPath: KeyPath<Self, Value>)
where Self: NSObject
{
let observer = self.observe(sourceKeyPath, options: [.initial, .new]) {object, change in
self[keyPath: targetKeyPath] = change.newValue!
}
self.observers.append(observer)
}
}

关于swift - 使用 keyPath 绑定(bind) 2 个属性(观察),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44730739/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com