如何更新此 class 以支持将键路径值绑定到函数?
How can I update this class to support binding a keypath value to a function?
我有一个 class 允许我将 属性 声明为 Bindable
let user: Bindable<User> = Bindable(someUser)
user.update(with: someNewUser)
......
user.bind(\.name, to: label, \.text)
这样做允许更改直接绑定到 UI 元素。
这是基于找到的一篇文章 here
import Foundation
final class Bindable<Value> {
private var observations = [(Value) -> Bool]()
private var lastValue: Value?
init(_ value: Value? = nil) {
lastValue = value
}
}
extension Bindable {
func update(with value: Value) {
lastValue = value
observations = observations.filter { [=12=](value) }
}
}
extension Bindable {
// Non Optionals
func bind<O: AnyObject, T>(_ sourceKeyPath: KeyPath<Value, T>, to object: O, _ objectKeyPath: ReferenceWritableKeyPath<O, T>) {
addObservation(for: object) { object, observed in
let value = observed[keyPath: sourceKeyPath]
object[keyPath: objectKeyPath] = value
}
}
// Optionals
func bind<O: AnyObject, T>(_ sourceKeyPath: KeyPath<Value, T>, to object: O, _ objectKeyPath: ReferenceWritableKeyPath<O, T?>) {
addObservation(for: object) { object, observed in
let value = observed[keyPath: sourceKeyPath]
object[keyPath: objectKeyPath] = value
}
}
}
private extension Bindable {
func addObservation<O: AnyObject>(for object: O, handler: @escaping (O, Value) -> Void) {
// If we already have a value available, give the handler access to it directly.
lastValue.map { handler(object, [=12=]) }
// Each observation closure returns a Bool that indicates
// whether the observation should still be kept alive,
// based on whether the observing object is still retained.
observations.append { [weak object] value in
guard let object = object else { return false }
handler(object, value)
return true
}
}
}
我想做的也是能够将 属性 绑定到一个函数。
绑定值的当前语法类似于 -
user.bind(\.name, to: label, \.text)
但我想对此进行扩展,以便该键路径中的 属性 可以调用一个方法。
有点像-
func doSomethingWithProp(_ prop: String) {
// do something
}
user.bind(\.name, to: doSomething)
在这种情况下,doSomething
可以为 NSAttributedString
调用一个助手,并接受 name
道具作为该助手中使用的参数。
我在 RxSwift
中使用 bind(onNext: ....)
看到了类似的东西。
我尝试使用 -
func bind<O: AnyObject, T, P>(_ sourceKeyPatch: KeyPath<Value, T>, to onNext: @escaping (P) -> Void) {
addObservation(for: onNext) { onNext, observed in
let value = observed[keyPath: sourceKeyPath]
onNext(value)
}
}
位我得到以下错误-
Generic parameter 'O' is not used in function signature
Generic parameter 'O' could not be inferred
这种可绑定方法期望有一些观察对象,但您没有。也就是说,它实际上并不关心那个对象是什么。这只是传递回处理程序的东西。因此,您可以通过使用 self
作为占位符对象,以这种方式在扩展中处理此问题:
func bind<T>(_ sourceKeyPath: KeyPath<Value, T>, onNext: @escaping (T) -> Void) {
addObservation(for: self) { object, observed in
let value = observed[keyPath: sourceKeyPath]
onNext(value)
}
}
就是说,这感觉有点乱,所以我可能会重新设计 Bindable 以原生支持它,并在其上构建对象绑定。通过调用处理程序,让私有 addObservation
少做一点:
private extension Bindable {
func addObservation(handler: @escaping (Value) -> Bool) { // <== Require a bool here
lastValue.map { handler([=11=]) }
observations.append { handler([=11=]) } // <== Just call the handler
}
}
并使所有 public 方法对对象做更多的检查,因此私有扩展不必知道它。:
extension Bindable {
// Non Optionals
func bind<O: AnyObject, T>(_ sourceKeyPath: KeyPath<Value, T>, to object: O, _ objectKeyPath: ReferenceWritableKeyPath<O, T>) {
addObservation { [weak object] observed in
guard let object = object else { return false } // <== Have to check object here instead
let value = observed[keyPath: sourceKeyPath]
object[keyPath: objectKeyPath] = value
return true
}
}
// Optionals
func bind<O: AnyObject, T>(_ sourceKeyPath: KeyPath<Value, T>, to object: O, _ objectKeyPath: ReferenceWritableKeyPath<O, T?>) {
addObservation { [weak object] observed in
guard let object = object else { return false }
let value = observed[keyPath: sourceKeyPath]
object[keyPath: objectKeyPath] = value
return true
}
}
// Function
func bind<T>(_ sourceKeyPath: KeyPath<Value, T>, onNext: @escaping (T) -> Void) {
addObservation { observed in
let value = observed[keyPath: sourceKeyPath]
onNext(value)
return true
}
}
}
您可以在此处进行更多重构以减少一些代码重复,但关键是要让原始处理程序做的事情更少。
请注意,在 iOS 13+ 中,这应该使用 Combine 来完成。它以更强大的方式完成这一切。
我有一个 class 允许我将 属性 声明为 Bindable
let user: Bindable<User> = Bindable(someUser)
user.update(with: someNewUser)
......
user.bind(\.name, to: label, \.text)
这样做允许更改直接绑定到 UI 元素。
这是基于找到的一篇文章 here
import Foundation
final class Bindable<Value> {
private var observations = [(Value) -> Bool]()
private var lastValue: Value?
init(_ value: Value? = nil) {
lastValue = value
}
}
extension Bindable {
func update(with value: Value) {
lastValue = value
observations = observations.filter { [=12=](value) }
}
}
extension Bindable {
// Non Optionals
func bind<O: AnyObject, T>(_ sourceKeyPath: KeyPath<Value, T>, to object: O, _ objectKeyPath: ReferenceWritableKeyPath<O, T>) {
addObservation(for: object) { object, observed in
let value = observed[keyPath: sourceKeyPath]
object[keyPath: objectKeyPath] = value
}
}
// Optionals
func bind<O: AnyObject, T>(_ sourceKeyPath: KeyPath<Value, T>, to object: O, _ objectKeyPath: ReferenceWritableKeyPath<O, T?>) {
addObservation(for: object) { object, observed in
let value = observed[keyPath: sourceKeyPath]
object[keyPath: objectKeyPath] = value
}
}
}
private extension Bindable {
func addObservation<O: AnyObject>(for object: O, handler: @escaping (O, Value) -> Void) {
// If we already have a value available, give the handler access to it directly.
lastValue.map { handler(object, [=12=]) }
// Each observation closure returns a Bool that indicates
// whether the observation should still be kept alive,
// based on whether the observing object is still retained.
observations.append { [weak object] value in
guard let object = object else { return false }
handler(object, value)
return true
}
}
}
我想做的也是能够将 属性 绑定到一个函数。
绑定值的当前语法类似于 -
user.bind(\.name, to: label, \.text)
但我想对此进行扩展,以便该键路径中的 属性 可以调用一个方法。
有点像-
func doSomethingWithProp(_ prop: String) {
// do something
}
user.bind(\.name, to: doSomething)
在这种情况下,doSomething
可以为 NSAttributedString
调用一个助手,并接受 name
道具作为该助手中使用的参数。
我在 RxSwift
中使用 bind(onNext: ....)
看到了类似的东西。
我尝试使用 -
func bind<O: AnyObject, T, P>(_ sourceKeyPatch: KeyPath<Value, T>, to onNext: @escaping (P) -> Void) {
addObservation(for: onNext) { onNext, observed in
let value = observed[keyPath: sourceKeyPath]
onNext(value)
}
}
位我得到以下错误-
Generic parameter 'O' is not used in function signature
Generic parameter 'O' could not be inferred
这种可绑定方法期望有一些观察对象,但您没有。也就是说,它实际上并不关心那个对象是什么。这只是传递回处理程序的东西。因此,您可以通过使用 self
作为占位符对象,以这种方式在扩展中处理此问题:
func bind<T>(_ sourceKeyPath: KeyPath<Value, T>, onNext: @escaping (T) -> Void) {
addObservation(for: self) { object, observed in
let value = observed[keyPath: sourceKeyPath]
onNext(value)
}
}
就是说,这感觉有点乱,所以我可能会重新设计 Bindable 以原生支持它,并在其上构建对象绑定。通过调用处理程序,让私有 addObservation
少做一点:
private extension Bindable {
func addObservation(handler: @escaping (Value) -> Bool) { // <== Require a bool here
lastValue.map { handler([=11=]) }
observations.append { handler([=11=]) } // <== Just call the handler
}
}
并使所有 public 方法对对象做更多的检查,因此私有扩展不必知道它。:
extension Bindable {
// Non Optionals
func bind<O: AnyObject, T>(_ sourceKeyPath: KeyPath<Value, T>, to object: O, _ objectKeyPath: ReferenceWritableKeyPath<O, T>) {
addObservation { [weak object] observed in
guard let object = object else { return false } // <== Have to check object here instead
let value = observed[keyPath: sourceKeyPath]
object[keyPath: objectKeyPath] = value
return true
}
}
// Optionals
func bind<O: AnyObject, T>(_ sourceKeyPath: KeyPath<Value, T>, to object: O, _ objectKeyPath: ReferenceWritableKeyPath<O, T?>) {
addObservation { [weak object] observed in
guard let object = object else { return false }
let value = observed[keyPath: sourceKeyPath]
object[keyPath: objectKeyPath] = value
return true
}
}
// Function
func bind<T>(_ sourceKeyPath: KeyPath<Value, T>, onNext: @escaping (T) -> Void) {
addObservation { observed in
let value = observed[keyPath: sourceKeyPath]
onNext(value)
return true
}
}
}
您可以在此处进行更多重构以减少一些代码重复,但关键是要让原始处理程序做的事情更少。
请注意,在 iOS 13+ 中,这应该使用 Combine 来完成。它以更强大的方式完成这一切。