Swift KeyPath

Swift KeyPath

swift是一门类型安全的编程语言,不像OC、Ruby等可以在运行时检查甚至改变某一种类型或者实现,swift在编译的时候,就明确一个变量的类型,但是有些时候,我们需要一些动态的特性,例如,我们不需要在乎某个对象的类型,只在乎其某些属性的类型,或者根本不需要在乎对象本身,只需要在乎其类型。swift为我们提供了这种机制KeyPath。

几种常用的KeyPath:

public class KeyPath : PartialKeyPath {
}

public class WritableKeyPath : KeyPath {
}

public class ReferenceWritableKeyPath : WritableKeyPath {
}

KeyPath是一种只读的,WritableKeyPath是可读可写的,ReferenceWritableKeyPath只能作用在引用类型(class)的可读可写的KeyPath。

使用KeyPath

例如我们有一个结构体:

struct Color: CustomStringConvertible {
    var red: UInt8
    var green: UInt8
    var blue: UInt8
    var alpha: UInt8
    
    var value: Int32 {
        return Int32(red) << 3 | Int32(green) << 2 | Int32(blue) << 1 | Int32(alpha)
    }
}

之前获取属性的方式,例如获取Colorvalue属性:

let color = Color.init(red: 255, green: 255, blue: 255, alpha: 255)
print(color.value)

使用KeyPath的方式如下:

print(color[keyPath: \.value])
// or
let keyPath: KeyPath = \.value
print(color[keyPath: keyPath])

通过KeyPath获取某一个对象的属性可以使用objectName[keyPath: \.propertyName]的方式,也可以通过let keyPath: KeyPath = \.value先创建KeyPath。
如果对象和属性是可变的话,可以使用WritableKeyPath来改变属性值:

var mutColor = Color(red: 1, green: 1, blue: 1, alpha: 1)
mutColor[keyPath: \.red] = 255
let mutKeyPath: WritableKeyPath = \.blue
mutColor[keyPath: mutKeyPath] = 255

用途

假设我们需要展示一个对象的所有数据,可以考虑使用KeyPath,传统的做法,例如展示一个Person的详细信息:

struct Person {
    var name: String
    var address: String
    var postCode: String
    var age: Int
}

之前的做法是:

func showPerson(p: Person) {
    print(p.name)
    print(p.address)
    print(p.postCode)
    print(p.age)
}
let tom = Person(...)
showPerson(p: tom)

但是这么做虽然没什么问题,只是不容易扩展,如果需要展示一辆汽车或者学生信息的话,就需要创建更多的方法。
可以使用KeyPath来构建一个新方法,该方法接受可变数量的KeyPath作为参数:

func showDetail(object: Element, keys: KeyPath ...) {
    for one in keys {
        print(object[keyPath: one])
    }
}
let p = Person.init(...)
showDetail(object: p, keys: \.name, \.postCode, \.address)

如果说某些数据不是String类型的话,可以通过为对象添加一个extension 来将某些属性转换成String

extension Person {
    var ageStr: String { return "\(self.age)" }
}
showDetail(object: p, keys: \.name, \.postCode, \.address, \.ageStr)

还有就是我们平常会用到排序,如果给你一组数据,并且需要按照数据的某个属性进行排序的话,也可以使用KeyPath来做通用方法:

extension Array {
    func sort(ascendingKey: KeyPath) -> Array {
        sorted { l, r -> Bool in
            return l[keyPath: ascendingKey] < r[keyPath: ascendingKey]
        }
    }
    
    func sort(descendingKey: KeyPath) -> Array {
        sorted { l, r -> Bool in
            return l[keyPath: descendingKey] > r[keyPath: descendingKey]
        }
    }
}

某些时候,需要把一个数组转换成字典,字典的Key是数据的某个属性:

extension Array {
    func map(mapKey: KeyPath) -> [Key: Element] {
        var result: [Key: Element] = [:]
        forEach { element in
            result[element[keyPath: mapKey]] = element
        }
        return result
    }
}

你可能感兴趣的:(Swift KeyPath)