[TOC]git
參考what's new in swift 5.0和細說 Swift 4.2 新特性:Dynamic Member Lookupgithub
dynamicMemberLookup是Swift4.2裏更新的一個特性翻譯出來就是動態成員查找。在使用@dynamicMemberLookup標記了對象後(對象、結構體、枚舉、protocol),實現了subscript(dynamicMember member: String)方法後咱們就能夠訪問到對象不存在的屬性。若是訪問到的屬性不存在,就會調用到實現的 subscript(dynamicMember member: String)方法,key 做爲 member 傳入這個方法。json
例如:swift
@dynamicMemberLookup
class Test {
subscript (dynamicMember member: String) -> String {
return "12321321"
}
subscript (dynamicMember member: String) -> Int {
return 455
}
}
let t = Test()
var s:String = t.name
var p: Int = t.age
print(s);
print(p);
複製代碼
輸出的結果爲 s = "12321321",p = 455數組
我再這個類裏面並無顯示的聲明 name 和 age 這兩個屬性可是他卻能夠獲得這兩個屬性。是由於當我將這個類標記爲 @dynamicMemberLookup 類裏面會實現**subscript (dynamicMember member: String) -> ?**這個方法。安全
若是沒有聲明@dynamicMemberLookup的話,執行的代碼確定會編譯失敗。很顯然做爲一門類型安全語言,編譯器會告訴你不存在這些屬性。可是在聲明瞭@dynamicMemberLookup後,雖然沒有定義 age等屬性,可是程序會在運行時動態的查找屬性的值,調用subscript(dynamicMember member: String)方法來獲取值。bash
這個屬性能夠被重載,會根據你要的返回值而經過類型推斷來選擇對應的subscript方法。例如app
@dynamicMemberLookup
struct Person {
subscript(dynamicMember member: String) -> String {
let properties = ["name": "Swift", "city": "B"]
return properties[member, default: ""]
}
subscript(dynamicMember member: String) -> Int {
return 18
}
}
let p = Person()
/***聲明常量必須聲明類型*/
let test:String = p.k;
print(p.nickname)
print(p.city)
print(test);
print(p.age)
複製代碼
輸出的結果爲 "Swift","b","undefined",18。 執行的時候必定要告訴編譯器你的常量是什麼類型的。dom
咱們知道了dynamicMemberLookup是什麼怎麼用,可是蘋果爲啥要推出這樣一種語法糖。函數
官方給出的例子是這樣的
@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裏面的值則須要
let json = JSON.stringValue("Example")
json[0]?["name"]?["first"]?.stringValue
複製代碼
可是聲明dynamicLookUp的就能夠這樣使用
json[0]?.name?.first?.stringValue
複製代碼
它是將自定義下標轉換爲簡單點語法的語法糖。 其實至關於執行了 json[0].name == json[0].subscript(dynamicMember member: "name")
經過這個方法拿到 json[0]字典key爲name對應的值
subscript(dynamicMember member: String) -> JSON? {
if case .dictionaryValue(let dict) = self {
return dict[member]
}
return nil
}
複製代碼
這個只是簡單的應用 在Swift5.0裏又推出了dynamicCallable這個特性。能夠動態的進行傳參。
SE-0216向@dynamicCallable 添加了一個新的@dynamicCallable屬性,該屬性帶來了將類型標記爲可直接調用的能力。它是語法糖,而不是任何類型的編譯器,有效地轉換此代碼:
let result = random(numberOfZeroes: 3)
let result = random.dynamicallyCall(withKeywordArguments: ["numberOfZeroes": 3])
複製代碼
以前,在Swift 4.2 中寫了一個叫作@dynamicMemberLookup的功能。@dynamicCallable是@dynamicMemberLookup的天然擴展,@dynamicMemberLookup而且具備相同的目的:使 Swift 代碼更容易與動態語言(如 Python 和 JavaScript)一塊兒工做 要將此功能添加到本身的類裏,須要添加@dynamicCallable屬性加上如下一@dynamicCallable種或兩種方法:
func dynamicallyCall(withArguments args: [Int]) -> Double
func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, Int>) -> Double
複製代碼
第一種是在調用沒有參數標籤的類型時使用的,第二種是在提供標籤時a(b, c)使用的(例如a(b: cat, c: dog) ). @dynamicCallable很是靈活地瞭解其方法接受和返回的數據類型,讓您從 Swift 的全部類型安全性中獲益,同時仍有一些可高級使用空間。所以,對於第一個方法(沒有參數標籤),您可使用任何符合ExpressibleByArrayLiteral的任何方法,如數組、數組切片和集;對於第二種方法(帶有參數標籤),您可使用任何符合ExpressibleByDictionaryLiteral文本,如字典和鍵值對。
注意:若是您之前沒有使用過KeyValuePairs那麼如今正是瞭解它們的好時機,由於它們@dynamicCallable很是有用。
KeyValuePairs在 Swift 5.0 以前,有點使人困惑地稱爲DictionaryLiteral是一種有用的數據類型,它提供了相似字典的功能,具備如下幾個優勢:
除了接受各類輸入外,您還能夠爲各類輸出提供多個重載 - 一個輸出能夠返回一個字符串,一個返回一個整數,等等。只要 Swift 可以解決使用哪個,就能夠混合和匹配全部您想要的。
下面是一個例子:
首先,下面是一個RandomNumberGenerator結構,根據傳入的輸入,生成介於 0 和特定最大值之間的數字:
struct RandomNumberGenerator {
func generate(numberOfZeroes: Int) -> Double {
let maximum = pow(10, Double(numberOfZeroes))
return Double.random(in: 0...maximum)
}
}
let random = RandomNumberGenerator()
let result = random.generate(numberOfZeroes: 0)
複製代碼
要將其切換到@dynamicCallable咱們將@dynamicCallable編寫相似內容:
@dynamicCallable
struct RandomNumberGenerator {
func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, Int>) -> Double {
let numberOfZeroes = Double(args.first?.value ?? 0)
let maximum = pow(10, numberOfZeroes)
return Double.random(in: 0...maximum)
}
}
let random = RandomNumberGenerator()
/// numberOfZeroes 能夠自定義
/// let result = random.dynamicallyCall(withKeywordArguments: ["numberOfZeroes": 3])
/// let result = random(numberOfZeroes: 3)
let result = random(numberOfZeroes: 0)
複製代碼
@dynamicCallable時須要注意一些重要的規則:
dynamicMemberLookup是Swift4.2裏更新的一個特性翻譯出來就是動態成員查找。在使用@dynamicMemberLookup標記了對象後(對象、結構體、枚舉、protocol),實現了subscript(dynamicMember member: String)方法後咱們就能夠訪問到對象不存在的屬性。若是訪問到的屬性不存在,就會調用到實現的 subscript(dynamicMember member: String)方法,key 做爲 member 傳入這個方法。 ynamicCallable屬性,該屬性帶來了將類型標記爲可直接調用的能力。它是語法糖
Swift 目前能夠」良好「的和 C、OC 交互。然而程序的世界裏還有一些重要的動態語言,好比 Python 、 JS,emmm,還有有實力可是不太主流的 Perl、Ruby。若是 swift 可以愉快的的調用 Python 和 JS 的庫,那麼毫無疑問會極大的拓展的 swift 的邊界。 這裏須要一點想象力,由於這個設計真正的意義是@dynamicMemberLookup、 @dynamicCallable組合起來用。經過@dynamicMemberLookup動態的返回一個函數,再經過@dynamicCallable來調用。從語法層面來說,這種姿態下 swift 完徹底全是一門動態語言。