我一直试图找到一个例子,但我所看到的并不适用于我的情况.
什么是以下代码的等价物:
object.addObserver(self, forKeyPath: "keyPath", options: [.new], context: nil)
override public func observeValue(
forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
}
Run Code Online (Sandbox Code Playgroud)
上面的代码有效,但我从SwiftLink收到警告:
使用Swift 3.2或更高版本时,首选基于新块的KVO API和键路径.
如果你能指出我正确的方向,我感激不尽.
我知道Swift中泛型的局限性以及它们存在的原因所以这不是关于编译器错误的问题.相反,我偶尔遇到的情况似乎应该可以通过可用资源的某种组合(即泛型,关联类型/协议等)来实现,但似乎无法找到解决方案.
在这个例子中,我试图为NSSortDescriptor提供一个Swift替换(只是为了好玩).当你只有一个描述符时它很完美,但是,正如通常使用NS版本一样,创建一个SortDescriptors数组以对多个键进行排序会很好.
这里的另一个试验是使用Swift KeyPaths.因为那些需要Value类型并且比较需要Comparable值,所以我很难找出在何处/如何定义类型以满足所有要求.
这可能吗?这是我提出的最接近的解决方案之一,但是,正如您在底部看到的那样,在构建阵列时它不足.
同样,我理解为什么这不起作用,但我很好奇是否有办法实现所需的功能.
struct Person {
let name : String
let age : Int
}
struct SortDescriptor<T, V:Comparable> {
let keyPath: KeyPath<T,V>
let ascending : Bool
init(_ keyPath: KeyPath<T,V>, ascending:Bool = true) {
self.keyPath = keyPath
self.ascending = ascending
}
func compare(obj:T, other:T) -> Bool {
let v1 = obj[keyPath: keyPath]
let v2 = other[keyPath: keyPath]
return ascending ? v1 < v2 : v2 < v1
}
}
let jim = Person(name: "Jim", age: 30)
let bob …Run Code Online (Sandbox Code Playgroud) 我很高兴找到Swift 3的实现#keyPath(),这将消除拼写错误并在编译时强制执行关键路径实际存在.比手动键入字符串要好得多.
https://github.com/apple/swift-evolution/blob/master/proposals/0062-objc-keypaths.md
class MyObject {
@objc var myString: String = "default"
}
// Works great
let keyPathString = #keyPath(MyObject.myString)
Run Code Online (Sandbox Code Playgroud)
在斯威夫特的文档列表类型被传递到#keyPath()作为"属性名称".
属性名称必须是对Objective-C运行时中可用的属性的引用.在编译时,键路径表达式由字符串文字替换.
是否可以单独保存此"属性名称",然后传递#keyPath()给创建一个字符串?
let propertyName = MyObject.myString // error. How do I save?
let string = #keyPath(propertyName)
Run Code Online (Sandbox Code Playgroud)
是否支持要求属于特定类型的属性名称?
// something like this
let typedPropertyName: MyObject.PropertyName = myString // error
let string = #keyPath(typedPropertyName)
Run Code Online (Sandbox Code Playgroud)
最终目标将与需要NSExpression关键路径的API进行交互.我想编写方便的方法,将有效的属性名称作为参数,而不是随机密钥路径字符串.理想情况下,由特定类型实现的属性名称.
func doSomethingForSpecificTypeProperty(_ propertyName: MyObject.PropertyName) {
let keyPathString = #keyPath(propertyName)
let expression = NSExpression(forKeyPath: keyPathString)
// ... …Run Code Online (Sandbox Code Playgroud) 我有一个枚举案例数组,其中每个案例都有一个keyPath属性,该属性返回AnyKeyPath与枚举案例同名的匹配类属性:
protocol PathAccessor: CodingKey {
var keyPath: AnyKeyPath { get }
static var allCases: [Self] { get }
init?(rawValue: Int)
}
extension PathAccessor {
static var allCases: [Self] {
var cases: [Self] = []
var index: Int = 0
while let element = Self.init(rawValue: index) {
cases.append(element)
index += 1
}
return cases
}
}
class Robot {
let name: String
var age: Int
var powered: Bool
var hasItch: Bool?
enum CodingKeys: Int, PathAccessor {
case …Run Code Online (Sandbox Code Playgroud) 我在使用新的KVO语法时遇到了一些麻烦.根据Apple文档:
为关键路径创建一个观察者并调用observe(_:options:changeHandler)方法.有关密钥路径的更多信息,请参阅密钥和密钥路径.
class MyObserver: NSObject {
@objc var objectToObserve: MyObjectToObserve
var observation: NSKeyValueObservation?
init(object: MyObjectToObserve) {
objectToObserve = object
super.init()
observation = observe(\.objectToObserve.myDate) { object, change in
print("Observed a change to \(object.objectToObserve).myDate, updated to: \(object.objectToObserve.myDate)")
}
}
}
let observed = MyObjectToObserve()
let observer = MyObserver(object: observed)
observed.updateDate()
Run Code Online (Sandbox Code Playgroud)
我正在初始化我的观察:
self.observation = self.webView!.observe(\.webView.isLoading, changeHandler: { (webView, observedChange) in
//code
})
Run Code Online (Sandbox Code Playgroud)
但是我收到了这个错误:
似乎应该可以使用KeyPaths 数组作为排序键,以使用任意数量的排序键对Swift结构数组进行排序。从概念上讲,这很简单。您定义了一个通用对象的KeyPath数组,其中唯一的限制是keypath中的属性为Comparable。
只要所有KeyPath都指向相同类型的属性,就可以了。但是,一旦您尝试使用指向数组中不同类型元素的KeyPath,它就会停止工作。
请参见下面的代码。我创建了一个具有2个Int属性和一个double属性的简单结构。我为实现函数的Array创建扩展,sortedByKeypaths(_:)
该函数指定可比较的通用类型PROPERTY。它需要一个kepath数组到某个对象元素,该对象指定了PROPERTY类型的属性。(可比较的属性。)
只要您使用一组KeyPath来调用具有相同类型的属性的函数,它就可以完美地工作。
但是,如果您尝试将键路径数组传递给不同类型的属性,则会引发错误“无法将类型'[PartialKeyPath]'的值转换为预期参数类型'[KeyPath]'”
由于数组包含异构键路径,因此由于类型擦除,数组会演变为类型为[[PartialKeyPath]],并且您不能使用PartialKeyPath从数组中获取元素。
有解决这个问题的方法吗?无法使用异构的KeyPath数组似乎严重限制了Swift KeyPath的用途
import UIKit
struct Stuff {
let value: Int
let value2: Int
let doubleValue: Double
}
extension Array {
func sortedByKeypaths<PROPERTY: Comparable>(_ keypaths: [KeyPath<Element, PROPERTY>]) -> [Element] {
return self.sorted { lhs, rhs in
var keypaths = keypaths
while !keypaths.isEmpty {
let keypath = keypaths.removeFirst()
if lhs[keyPath: keypath] != rhs[keyPath: keypath] {
return lhs[keyPath: keypath] < rhs[keyPath: keypath]
}
}
return true
}
}
}
var …Run Code Online (Sandbox Code Playgroud) 有没有办法从 Swift 4 中的字符串创建 Keypath 以通过其路径或变量名访问结构中的值
最后我发现我应该使用 CodingKeys 而不是 KeyPaths 来通过 String 访问结构的变量的值
提前致谢,迈克尔
考虑以下代码片段
class A {
var value: Int?
}
let a: A? = A()
let kp = \A.value
a?[keyPath: kp] = 10
print(a?.value)
Run Code Online (Sandbox Code Playgroud)
这完美地工作Optional(10)并按预期打印。在我的实际应用程序中,我试图以这种方式设置的字段被声明为Date?,它会导致一些奇怪的错误。我的实际应用程序的 MWE 是这样的:
class A {
var value: Date?
}
let a: A! = A()
let kp = \A.value
a?[keyPath: kp] = Date() // Line with error
print(a?.value)
Run Code Online (Sandbox Code Playgroud)
然而,编译器抱怨突出显示的行并说:
可选类型“日期?”的值 必须解包为“日期”类型的值
修复:使用“??”合并 当可选值包含“nil”时提供默认值
修复:使用 '!' 强制解包 如果可选值包含“nil”则中止执行
这是编译器的错误,我们可以期望在最终版本之前修复,还是我对关键路径不了解?
我正在使用 Xcode 11 beta 3,但我在 beta 2 中也遇到了同样的问题。如果它有用,实际代码在这里。
我编写了一个程序来计算视图的总宽度/高度(有时我想要总宽度,有时我想要总高度)。唯一的问题是:如果我计算宽度,我想10在总数中添加一个额外的值。这是我当前的代码:
func calculateLengthOfAllViews(calculatingWidth: Bool) {
let views = [
UIView(frame: CGRect.zero),
UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 50)),
UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
]
var totalLength: CGFloat = 0
if calculatingWidth {
totalLength += 10 /// add extra 10 if calculating width
} else {
totalLength += 0
}
for view in views { /// add each view's width/height
let length: CGFloat
if calculatingWidth {
length = view.frame.width
} else {
length …Run Code Online (Sandbox Code Playgroud) 如何从嵌套类创建 KeyPath?我不知道如何使用反斜杠访问嵌套类。
示例代码:
class Config {
var clients: Array<Client> = [Client()]
// Nested Class
class Client {
public var name: String = "SomeName"
}
}
var conf = Config()
func accessValue<T, U>(
_ keyPath: ReferenceWritableKeyPath<Config, Array<T>>,
valuePath: ReferenceWritableKeyPath<T ,U>) -> Any {
let member: Array<T> = conf[keyPath: keyPath]
let firstMember: T = member.first!
let value = firstMember[keyPath: valuePath]
return value
}
// How should I call this function?
print(accessValue(\Config.clients, valuePath: wanna_know))
// Should print "SomeName"
Run Code Online (Sandbox Code Playgroud)
提前致谢!