Key Paths 新语法
key-path 通常是用在键值编码(KVC)与键值观察(KVO)上的。
历史版本
Swift3 之前使用的是 String 类型的 key-Path
//用户类
class User: NSObject{
@objc var name:String = "" //姓名
@objc var age:Int = 0 //年龄
}
//创建一个User实例对象
let user1 = User()
user1.name = "hangge"
user1.age = 100
//使用KVC取值
let name = user1.value(forKey: "name")
print(name)
//使用KVC赋值
user1.setValue("hangge.com", forKey: "name")
到了Swift3新增了 #keyPath() 写法
//用户类
class User: NSObject{
@objc var name:String = "" //姓名
@objc var age:Int = 0 //年龄
}
//创建一个User实例对象
let user1 = User()
user1.name = "hangge"
user1.age = 100
//使用KVC取值
let name = user1.value(forKeyPath: #keyPath(User.name))
print(name)
//使用KVC赋值
user1.setValue("hangge.com", forKeyPath: #keyPath(User.name))
swift 4 中直接用 \ 作为开头创建 KeyPath
新的方式不仅使用更加简单,而且有如下优点:
类型可以定义为 class、struct
定义类型时无需加上 @objc 等关键字
性能更好
类型安全和类型推断,例如:user1.value(forKeyPath: #keyPath(User.name)) 返回的类型是 Any,user1[keyPath: \User.name] 直接返回 String 类型
可以在所有值类型上使用
- 比如上面的样例在 Swift4 中可以这么写:
//用户类
class User: NSObject{
var name:String = "" //姓名
var age:Int = 0 //年龄
}
//创建一个User实例对象
let user1 = User()
user1.name = "hangge"
user1.age = 100
//使用KVC取值
let name = user1[keyPath: \User.name]
print(name)
//使用KVC赋值
user1[keyPath: \User.name] = "hangge.com"
- keyPath 定义在外面也是可以的:
let keyPath = \User.name
let name = user1[keyPath: keyPath]
print(name)
user1[keyPath: keyPath] = "hangge.com"
- 可以使用 appending 方法向已定义的 Key Path 基础上填加新的 Key Path。
let keyPath1 = \User.phone
let keyPath2 = keyPath1.appending(path: \.number)
类与协议的组合类型
在 Swift4 中,可以把类(Class)和协议(Protocol)用 & 组合在一起作为一个类型使用。
使用样例1:
protocol MyProtocol { }
class View { }
class ViewSubclass: View, MyProtocol { }
class MyClass {
var delegate: (View & MyProtocol)?
}
let myClass = MyClass()
myClass.delegate = ViewSubclass() //这个编译正常
myClass.delegate = View() //这个编译报错:
使用样例2:
protocol Shakeable {
func shake()
}
extension UIButton: Shakeable {
func shake() {
/* ... */
}
}
extension UISlider: Shakeable {
func shake() {
/* ... */
}
}
func shakeEm(controls: [UIControl & Shakeable]) {
for control in controls where control.isEnabled {
control.shake()
}
}