Swift学习之KeyPath

一、什么是KeyPath

KeyPath:一个指定类型下的结果值的类型的路径
class KeyPath

struct Student{
    var name:String = ""
   let nameKeyPath:KeyPath = \Student.name
}
  • keyPath是对实例属性的一种引用,注意是对实例的属性的引用,而不是对属性值的引用。上面Student例子可以说是对实例Student属性name值的引用,而nameKeyPaths是对实例属性name的引用,它表示name是存储在Student的一个String而已,至于具体的值是多少,我们并不知道。
    1、 keyPath不需要实例就能表示属性位置及类型
    2、 keyPath解释了,哪个位置有什么类型的东西
  • keyPath的组成: \ 类型名称 . 属性名称

二、KeyPath的使用

struct Student{
    var name:String = ""
}

 let s = Student(name: "张三")
  let nameKeyPath: KeyPath = \Student.name        
  let name = s[keyPath: nameKeyPath]     
   print(name)
  • 实例对象可以通过object[keyPath:path]获取属性值
    上面的例子是最基本的使用,然而还看不出其强大之处,下面继续。

代替闭包

        let s = Student(name: "张三")
        let s1 = Student(name: "李四")
        let array = [s,s1]
        let names = array.map {$0.name}

上面map闭包获取所有的名字可以用keyPath代替:

  let s = Student(name: "张三")
       let s1 = Student(name: "李四")
       
       let array = [s,s1]
       let names = array.map {$0.name}
       let namess = array.map(\.name)
  • 字面表达.name, 虽然看起来是个省略类型的keyPath,Swift对这种结构尝试会自动转换成{
    object in
    object[keypath:keypath)
    }
  • 字面表达\Student.name,是个很明确的keyPath类型,Swift就不会再去转换,map那里面需要一个闭包,所以array.map(\Student.name)就会报错

类似协议封装的效果

import UIKit
struct CellConfigurator {
    let titleKeyPath: KeyPath
    let descKeyPath: KeyPath
    func configure(_ cell: UITableViewCell?, for model: Model) {
        cell?.textLabel?.text = model[keyPath: titleKeyPath]
        cell?.detailTextLabel?.text = model[keyPath: descKeyPath]
    }
}
struct Student{
    var title:String
    var desc:String
}
class ViewController: UIViewController {
  
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        let s = Student(title: "111", desc:"22")
     
        let cellConfigure = CellConfigurator(titleKeyPath: \Student.title, descKeyPath: \Student.desc)
        cellConfigure.configure(UITableViewCell(), for: s)
    }
}

三、KeyPath的类型

  • 1、KeyPath,Root可以是任何指定类型,Value属性指定类型,可读。
  • 2、WritableKeyPath,Root只可以是指定值类型,Value属性指定类型必须是var,可读可写
  • 3、ReferenceWritableKeyPath,Root只可以是指定引用类型,Value属性指定类型必须是var,可读可写
struct Cat{
    var name:String  
}
var cat = Cat(name: "hhh")
let nameKeyPath:WritableKeyPath = \Cat.name
print(cat[keyPath: nameKeyPath])
cat[keyPath: nameKeyPath] = "aaa"
print(cat.name)
class Cat{
    var name:String = "111"  
}
var cat = Cat()
let nameKeyPath:ReferenceWritableKeyPath = \Cat.name
print(cat[keyPath: nameKeyPath])
cat[keyPath: nameKeyPath] = "aaa"
print(cat.name)

Converting to functions

class ListViewController {
    private var items = [Item]() { didSet { render() } }

    func loadItems() {
        loader.load { [weak self] items in
            self?.items = items
        }
    }
}

上面的例子,我们通常为了避免循环引用[weak self]是必不可少的,这里我们就可以通过ReferenceWritableKeyPath来更加巧妙地解决这个问题;

func setter(for object: Object, keyPath: ReferenceWritableKeyPath) -> (Value) -> Void {
    return { [weak object] value in
        object?[keyPath: keyPath] = value
    }
}

下面让我们去我们改写原来的代码

class ListViewController {
    private var items = [Item]() { didSet { render() } }

    func loadItems() {
        loader.load(then: setter(for: self, keyPath: \.items))
    }
}

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