什么是 @dynamicMemberLookup
字面意思来理解就是动态成员查找
, 简单理解其目的就是动态来获取属性的值。
// 注解要使用的类或者结构体支持动态成员查找
@dynamicMemberLookup
struct Worker {
// 必须要实现的方法。返回值类型可自定义
subscript(dynamicMember member: String) -> String {
let properties = ["name": "LiaoWorking", "city": "Shanghai"]
return properties[member, default: ""]
}
}
let worker = Worker()
let name = worker.name // liaoworking
看到这里你肯定会一脸黑人问号❓❓❓
Worker没有name这个属性
我为什么会取到name的值。
难道编译器就不检查一下么。
这里编译器的确就管都不管一下了。 dynamic
和lookup
这两个词的意思马上就体现出来了。动态去查找
。
Demo中当执行下面这行代码时
let name = worker.name
实际上调用了我们实现的subscript方法,把name当”name”参数传递进去。 所以就得到了 name 为 liaoworking的结果。
下面再来看看关于@dynamicMemberLookUp更多的使用方法。
- 多类型的返回
// 注解说明要使用的类或者结构体支持动态成员查找
@dynamicMemberLookup
struct Worker {
// 返回值为String
subscript(dynamicMember member: String) -> String {
let properties = ["age": "17"]
return properties[member, default: ""]
}
// 返回值为Int
subscript(dynamicMember member: String) -> Int {
let properties = ["age": 18]
return properties[member, default: -1]
}
}
let worker = Worker()
// 这里系统可根据类型判断 来调用不同的subscript实现
let ageString: String = worker.age // “17”
let ageInt: Int = worker.age //19
上面的Demo具体使用场景还不太明确,只是有这样的用法。这里提一下。
- 下面这个Demo就很能说明@dynamicMemberLookup 的一个有用场景。
我们来创建一个JSON解析Struct
@dynamicMemberLookup
enum JSON {
case intValue(Int)
case stringValue(String)
case arrayValue(Array<JSON>)
case dictionaryValue(Dictionary<String, JSON>)
var stringValue: String? {
if case .stringValue(let str) = self {
return str
}
return nil
}
subscript(index: Int) -> JSON? {
if case .arrayValue(let arr) = self {
return index < arr.count ? arr[index] : nil
}
return nil
}
subscript(key: String) -> JSON? {
if case .dictionaryValue(let dict) = self {
return dict[key]
}
return nil
}
subscript(dynamicMember member: String) -> JSON? {
if case .dictionaryValue(let dict) = self {
return dict[member]
}
return nil
}
}
如果没有@dynamicMemberLookup关键字。我们操作一个JSON实例就会像下面这样:
json[0]?["name"]?["first"]?.stringValue
当使用@dynamicMemberLookup关键字后
json[0]?.name?.first?.stringValue
这样的语法糖可以让你的代码语义话更强。阅读起来更方便。
你可能会想为什么会有 @dynamicMemberLookUp 这种迷幻的存在
,在第一个Demo中直接定义一个属性name不就好了么。
其实@dynamicMemberLookUp的设计不单单是为了纯Swift的工程而考虑,像在机器学习,或者与其他语言之间相互桥接会有很大的用处。 这对于Swift语言来说还是很有帮助的。
参考资料:
https://www.hackingwithswift.com/articles/55/how-to-use-dynamic-member-lookup-in-swift
https://www.swiftbysundell.com/tips/combining-dynamic-member-lookup-with-key-paths