swift动态成员查找 @dynamicMemberLookup

看Alamofire源码的时候,看到@propertyWrapper和@dynamicMemberLookup,这篇说下dynamicMemberLookup

Swift 4.2 中引入了一个新的语法@dynamicMemberLookup(动态成员查找)。使用@dynamicMemberLookup标记了目标(类、结构体、枚举、协议),实现subscript(dynamicMember member: String)方法后我们就可以访问到对象不存在的属性。

  • @dynamicMemberLookup:可标记类、结构体、枚举、协议
  • subscript(dynamicMember member: String):实现该方法,可以像数组和字典一样,用下标的方式去访问属性,通过所请求属性的字符串名得到并返回想要的值
  • 可提高与 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类型值
  • 声明了@dynamicMemberLookup后,即使属性没有定义,但是程序会在运行时动态的查找属性的值,调用subscript(dynamicMember member: String)方法来获取值。
  • 如果类、结构体有对应的属性, 会优先查找属性, 属性查找不到 才会到dynamicMemberLookup 流程

  • subscript(dynamicMember member: 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)") // 直接访问成员属性的属性

返回值多类型查找

既然是动态查找,如果两个属性类型不同,怎么办?解决办法是重载subscript(dynamicMember member: String)方法。和泛型的逻辑类似,通过类型推断来选择对应的方法。但是此时调用的时候,所有属性必须显示声明类型,否则会报错

@dynamicMemberLookup
struct Person {
    
    subscript(dynamicMember member: String) -> String {
        let properties = ["name":"Zhangsan", "sex": "男"]
        return properties[member, default: "unknown property"]
    }
    
    
    subscript(dynamicMember member: String) -> Int {
        let properties = ["age": 20]
        return properties[member, default: 0]
    }
    
}

let p = Person()
// 类型必须明确
let name: String = p.name
print(name) // 打印 Zhangsan
let age: Int = p.age
print(age) // 打印 20
let sex: String = p.sex
print(sex) // 打印 男

它让people.job成为了可能。这只是一个用于讲解动态成员查找的简单案例,但在实际开发中,在特定场合可以发挥作用,正常的场景我们最好不要这么做。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值