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" 複製代碼
因此聲明在類上的時候必定要特別謹慎。markdown
固然若是想害同事,在BaseViewController
裏聲明是個好主意。 app
這個特性的感受就是乍一看很厲害的樣子,仔細一看好像就這麼回事,再冷靜想一想彷佛沒有這麼簡單。
這個東西本質上只是一個語法糖,和數組的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