swift - 如何更新此类以支持将键路径值绑定到函数?
问题描述
我有一个类允许我将属性声明为Bindable
let user: Bindable<User> = Bindable(someUser)
user.update(with: someNewUser)
......
user.bind(\.name, to: label, \.text)
这样做允许将更改直接绑定到 UI 元素。
这是基于在此处找到的一篇文章
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 { $0(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, $0) }
// 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)
}
}
位我收到以下错误-
函数签名中未使用通用参数“O”
无法推断通用参数“O”
解决方案
这种 Bindable 方法期望有一些观察对象,但你没有。也就是说,它实际上并不关心那个对象是什么。它只是传回给处理程序的东西。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($0) }
observations.append { handler($0) } // <== Just call the handler
}
}
并使所有公共方法对对象进行更多检查,因此私有扩展不必知道它。:
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 来完成。它以更强大的方式完成这一切。
推荐阅读
- powershell - 通过 Powershell 将文本文件内容与文件夹内容进行比较
- php - 如何计算非重复字符,不包括 PHP 中重复的字符
- mysql - MySQL from_unixtime 现在和午夜
- android - 如何在 espresso 测试中获得 Activity?
- kubernetes - 我应该如何配置我的服务来访问 grafana:localhost:3000 with minikube
- python - sys.stdin 什么时候等待输入?
- c - 带 MSP430 的 PWM“脉冲”
- elasticsearch - 不翻转的 Elasticsearch 索引生命周期策略问题
- kubernetes-helm - Helm 中的 fullnameOverride 和 nameOverride 有什么区别?
- spring-boot - Spring数据jdbc错误BadSqlGrammarException: PreparedStatementCallback; 糟糕的 SQL 语法,同时尝试保存简单的聚合