Swift 4.2 的新特性这两篇文章已经介绍的很清楚了:WWDC 2018:Swift 更新了什么,Swift 4.2 新特性更新。可是 4.2 中实现的 dynamic member lookup 苹果在 WWDC 上却彻底没有提到。然而我认为这是一个对将来有着重要影响的特性,因此这里单独介绍一下。python
这个特性中文能够叫动态查找成员。在使用@dynamicMemberLookup
标记了对象后(对象、结构体、枚举、protocol),实现了subscript(dynamicMember member: String)
方法后咱们就能够访问到对象不存在的属性。若是访问到的属性不存在,就会调用到实现的 subscript(dynamicMember member: String)
方法,key 做为 member 传入这个方法。 好比咱们声明了一个结构体,没有声明属性。git
@dynamicMemberLookup
struct Person {
subscript(dynamicMember member: String) -> String {
let properties = ["nickname": "Zhuo", "city": "Hangzhou"]
return properties[member, default: "undefined"]
}
}
//执行如下代码
let p = Person()
print(p.city)
print(p.nickname)
print(p.name)
复制代码
若是没有声明@dynamicMemberLookup
的话,执行的代码确定会编译失败。很显然做为一门类型安全语言,编译器会告诉你不存在这些属性。可是在声明了@dynamicMemberLookup
后,虽然没有定义 city
等属性,可是程序会在运行时动态的查找属性的值,调用subscript(dynamicMember member: String)
方法来获取值。程序员
Swift 面世时就大谈本身的安全特性,如今来了这么一个无限制访问的成员万一返回的是nil
不就闪退了?是的,出于安全的缘由,若是实现了这个特性,你就不能返回可选值。必须处理好意料外的状况,必定要有值返回。不像常规的subscript
方法能够返回可空的值。github
这个方法能够被重载。和泛型的逻辑相似,会根据你要的返回值而经过类型推断来选择对应的subscript
方法。json
@dynamicMemberLookup
struct Person {
subscript(dynamicMember member: String) -> String {
let properties = ["nickname": "Zhuo", "city": "Hangzhou"]
return properties[member, default: "undefined"]
}
subscript(dynamicMember member: String) -> Int {
return 18
}
}
复制代码
可是执行的时候就必定要告诉编译器你要获取的属性是什么类型的,不然会编译错误。swift
let p = Person()
let age: Int = p.age
print(age) // 18
复制代码
Swift 中函数是一等公民,因此返回函数也是能够的。数组
@dynamicMemberLookup
struct Person {
subscript(dynamicMember member: String) -> (_ input: String) -> Void {
return {
print("Hello! I live at the address \($0).")
}
}
}
复制代码
须要注意的是若是声明在类上,那么他的子类也会具备动态查找成员的能力。安全
@dynamicMemberLookup
class User {
subscript(dynamicMember member: String) -> String {
return "user"
}
}
class Developer: User { }
let dev = Developer()
dev.name // "user"
复制代码
因此声明在类上的时候必定要特别谨慎。app
固然若是想害同事,在BaseViewController
里声明是个好主意。 dom
这个特性的感受就是乍一看很厉害的样子,仔细一看好像就这么回事,再冷静想一想彷佛没有这么简单。
这个东西本质上只是一个语法糖,和数组的subscript
相似。
let numbers = [1, 2]
let firstItem = number[0]
//这个语法最后仍是调用到了一个方法,若是没有这种写法,相似 oc 的时候就须要显式的调用一个方法
NSNumber *firstItem = [numnber obbjectAtIndex: 0];
复制代码
原来你须要显式声明字符串参数的地方,能够不用是字符串的形式,能够直接用点语法访问。官方举的例子是 JSON 的使用。 常规的写法是这样的:
json[0]?["name"]?["first"]?.stringValue
复制代码
若是像这样定义动态查找成员:
@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
}
}
复制代码
那么写起来就会是这样:
json[0]?.name?.first?.stringValue
复制代码
这个功能的实现原理很简单,就是编译器帮助你把点语法转化为下标的语法:
a = someValue.someMember
//编译器处理后
a = someValue[dynamicMember: "someMember"]
复制代码
动态属性其实并不陌生,回忆一下 OC 里的属性就是动态合成的。声明了@property
后,编译器帮你生成get、set方法。与之相似,在声明了动态查找成员后,编译器帮你转换成了对应的方法。
若是你觉得这只是一个语法糖,那你就错了。
这个 pr 是由已经离开苹果加入谷歌的 swift 创始人 CL 提出的。他不只提了这个 pr,并且还本身实现了。果真是 swift 是亲儿子,身在曹营还不忘为 swift 添砖加瓦。并且大佬不只提了这个,还提了一个 @dynamicCallable 。 当你给一个对象标记@dynamicCallable
后,能够动态的给传参。
// 常规操做
a = someValue(keyword1: 42, "foo", keyword2: 19)
// dynamicallyCall
a = someValue.dynamicallyCall(withKeywordArguments: [
"keyword1": 42, "": "foo", "keyword2": 19
])
复制代码
是的,这很 JS。
大佬就是大佬,要啥有啥。目前 @dynamicCallable
的进度已经在 review 中,也许 5.0 的时候可以上?我猜想 swift 团队想这两个特性都开发完后一块儿宣布因此此次发布会没有介绍。
Swift 目前能够”良好“的和 C、OC 交互。然而程序的世界里还有一些重要的动态语言,好比 Python 、 JS,emmm,还有有实力可是不太主流的 Perl、Ruby。若是 swift 可以愉快的的调用 Python 和 JS 的库,那么毫无疑问会极大的拓展的 swift 的边界。
这里须要一点想象力,由于这个设计真正的意义是@dynamicMemberLookup
、 @dynamicCallable
组合起来用。经过@dynamicMemberLookup
动态的返回一个函数,再经过@dynamicCallable
来调用。从语法层面来说,这种姿态下 swift 完彻底全是一门动态语言。
@dynamicCallable @dynamicMemberLookup
class WeiSuoYuWei {
}
let niuBi = WeiSuoYuWei()
niuBi.someMethod.dynamicallyCall(withKeywordArguments: ["wei_suo_yu_wei": true])
复制代码
就像上面的代码展现的,你没必要声明过someMethod
也能够经过动态特性调用到,合法的传参。真的能够随心所欲!
听说谷歌的 TensorFlow For Swift 可以顺利的开发就是依靠了这个特性。CL 是这么说的:
While this is a syntactic sugar proposal, we believe that this expands Swift to be usable in important new domains
语法糖上的一小步,swift 的一大步!
How to use Dynamic Member Lookup in Swift – Hacking with Swift
SE 195: Introduce User-defined “Dynamic Member Lookup” Types