@dynamicMemberLookup(动态成员查找)
动态成员查找是 Swift 中的一项功能特性,可提高与 Python 或 Javascript 等动态语言的互操作性。它允许动态成员查找调用看起来像访问类型属性的常规调用:
let people = People() let name = people.name // 像访问属性一样
name是从字典中查找的,而不是作为 People 的属性访问的。
如何使用动态成员查找?
要在你的自定义类型中使用动态成员查找,请使用 @dynamicMemberLookup标注你的类型,并实现如下方法:
subscript(dynamicMember:)
该方法接收一个String或KeyPath类型参数
我们来看看People是如何实现动态成员查找的,下面是一个demo:
@dynamicMemberLookup struct People { private var map = ["name": "drbox", "job": "developer"] subscript(dynamicMember key: String) -> String { map[key] ?? "undefine" } }
现在你可以像访问它的属性一样查找 People 对象的成员。
let people = People() let name = people.name let job = people.job
我们分析一下People是如何实现动态成员查找的
- 首先我们使用@dynamicMemberLookup标注了People类型
- 实现了subscript(dynamicMember:)方法,该方法我们接收一个String类型的参数,并返回一个String类型值
现在你已经了解了动态成员查找的作用以及如何实现它。让我们通过KeyPath来看看另一个有用的例子。
struct Info { let job: String let homeAddr: String } struct People { let name: String let info: Info } let info = Info(job: "developer", homeAddr: "beijing") let people = People(name: "drbox", info: info) print("job: \(people.info.job)") // 访问成员属性的属性
通过调用people.info.job可以访问Info的属性值。但是,如果你想直接通过people.job 而不是people.info.job 调用它怎么办?
这时动态成员查找的KeyPath就派上用场了。
@dynamicMemberLookup struct People { let name: String let info: Info subscript<T>(dynamicMember path: KeyPath<Info, T>) -> T { info[keyPath: path] } } let info = Info(job: "developer", homeAddr: "beijing") let people = People(name: "drbox", info: info) print("job: \(people.job)") // 直接访问成员属性的属性
以上就是我们演示如何实现动态成员查找的例子,它让people.job成为了可能。这只是一个用于讲解动态成员查找的简单案例,但在实际开发中,我们最好不要这么做。
而动态成员查找在实际项目中的运用,你可以参考RxCocoa中Reactive,它运用动态成员查找,巧妙的为任何一个AnyObject类型的对象,增加了属性的Binder观察者。
@dynamicMemberLookup public struct Reactive<Base> { /// Base object to extend. public let base: Base /// Creates extensions with base object. /// /// - parameter base: Base object. public init(_ base: Base) { self.base = base } /// Automatically synthesized binder for a key path between the reactive /// base and one of its properties public subscript<Property>(dynamicMember keyPath: ReferenceWritableKeyPath<Base, Property>) -> Binder<Property> where Base: AnyObject { Binder(self.base) { base, value in base[keyPath: keyPath] = value } } }
例如:绑定UIView的背景色
要得到你必须要付出,要付出你还要学会坚持,如果你真的觉得很难,那你就放弃,但是你放弃了就不要抱怨,我觉得人生就是这样,世界真的是平等的,每个人都要通过自己的努力,去决定自己生活的样子。