Swift新特性dynamicMemberLookup和dynamicCallable

[TOC]

參考what's new in swift 5.0細說 Swift 4.2 新特性:Dynamic Member Lookup

@dynamicMemberLookup

@dynamicMemberLookup是什么

dynamicMemberLookup是Swift4.2里更新的一個特性翻譯出來就是動態(tài)成員查找葡盗。在使用@dynamicMemberLookup標記了對象后(對象偷卧、結構體拯爽、枚舉锅纺、protocol)管行,實現(xiàn)了subscript(dynamicMember member: String)方法后我們就可以訪問到對象不存在的屬性。如果訪問到的屬性不存在,就會調(diào)用到實現(xiàn)的 subscript(dynamicMember member: String)方法轿曙,key 作為 member 傳入這個方法。

例如:

 @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 類里面會實現(xiàn)subscript (dynamicMember member: String) -> 背捌?這個方法续挟。

如果沒有聲明@dynamicMemberLookup的話,執(zhí)行的代碼肯定會編譯失敗碑幅。很顯然作為一門類型安全語言,編譯器會告訴你不存在這些屬性。但是在聲明了@dynamicMemberLookup后陌僵,雖然沒有定義 age等屬性,但是程序會在運行時動態(tài)的查找屬性的值创坞,調(diào)用subscript(dynamicMember member: String)方法來獲取值碗短。

這個屬性可以被重載,會根據(jù)你要的返回值而通過類型推斷來選擇對應的subscript方法摆霉。例如

@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豪椿。 執(zhí)行的時候一定要告訴編譯器你的常量是什么類型的。

@dynamicMemberLookup有啥用

我們知道了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

它是將自定義下標轉換為簡單點語法的語法糖。
其實相當于執(zhí)行了
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這個特性婉支⊙煊纾可以動態(tài)的進行傳參。

dynamicCallable

@dynamicCallable是什么

SE-0216向@dynamicCallable 添加了一個新的@dynamicCallable屬性,該屬性帶來了將類型標記為可直接調(diào)用的能力向挖。它是語法糖,而不是任何類型的編譯器,有效地轉換此代碼:

let result = random(numberOfZeroes: 3)

let result = random.dynamicallyCall(withKeywordArguments: ["numberOfZeroes": 3])

之前,在Swift 4.2 中寫了一個叫做@dynamicMemberLookup的功能蝌以。@dynamicCallable是@dynamicMemberLookup的自然擴展,@dynamicMemberLookup并且具有相同的目的:使 Swift 代碼更容易與動態(tài)語言(如 Python 和 JavaScript)一起工作
要將此功能添加到自己的類里,需要添加@dynamicCallable屬性加上以下一@dynamicCallable種或兩種方法:

func dynamicallyCall(withArguments args: [Int]) -> Double

func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, Int>) -> Double

第一種是在調(diào)用沒有參數(shù)標簽的類型時使用的,第二種是在提供標簽時a(b, c)使用的(例如a(b: cat, c: dog) ).
@dynamicCallable非常靈活地了解其方法接受和返回的數(shù)據(jù)類型,讓您從 Swift 的所有類型安全性中獲益,同時仍有一些可高級使用空間。因此,對于第一個方法(沒有參數(shù)標簽),您可以使用任何符合ExpressibleByArrayLiteral的任何方法,如數(shù)組何之、數(shù)組切片和集;對于第二種方法(帶有參數(shù)標簽),您可以使用任何符合ExpressibleByDictionaryLiteral文本,如字典和鍵值對跟畅。

注意:如果您以前沒有使用過KeyValuePairs那么現(xiàn)在正是了解它們的好時機,因為它們@dynamicCallable非常有用。

KeyValuePairs在 Swift 5.0 之前,有點令人困惑地稱為DictionaryLiteral是一種有用的數(shù)據(jù)類型,它提供了類似字典的功能,具有以下幾個優(yōu)點:

  1. 您的密鑰不需要符合Hashable.
  2. 您可以使用重復的鍵添加項溶推。(不會覆蓋自定中添加的值)
  3. 添加項的順序將保留徊件。(是DictionAry變有序)

除了接受各種輸入外,您還可以為各種輸出提供多個重載 - 一個輸出可以返回一個字符串,一個返回一個整數(shù),等等。只要 Swift 能夠解決使用哪一個,就可以混合和匹配所有您想要的蒜危。

下面是一個例子:

首先,下面是一個RandomNumberGenerator結構,根據(jù)傳入的輸入,生成介于 0 和特定最大值之間的數(shù)字:

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編寫類似內(nèi)容:

@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使用注意

@dynamicCallable時需要注意一些重要的規(guī)則:

  1. 您可以將其應用于結構虱痕、枚舉、類和協(xié)議辐赞。
  2. 如果使用withKeywordArguments:并且不使用withArguments:您的類型仍然可以在沒有參數(shù)標簽的情況下調(diào)用 - 您只會獲得鍵的空字符串部翘。
  3. 如果withKeywordArguments:或與withArguments:被標記為throwing,調(diào)用類型也將throwing
  4. 不能@dynamicCallable添加到擴展,只能添加類型的主要定義响委。
  5. 您仍然可以向類型添加其他方法和屬性,并正常使用它們新思。

總結

dynamicMemberLookup是Swift4.2里更新的一個特性翻譯出來就是動態(tài)成員查找窖梁。在使用@dynamicMemberLookup標記了對象后(對象、結構體表牢、枚舉窄绒、protocol),實現(xiàn)了subscript(dynamicMember member: String)方法后我們就可以訪問到對象不存在的屬性崔兴。如果訪問到的屬性不存在彰导,就會調(diào)用到實現(xiàn)的 subscript(dynamicMember member: String)方法,key 作為 member 傳入這個方法敲茄。
ynamicCallable屬性,該屬性帶來了將類型標記為可直接調(diào)用的能力位谋。它是語法糖

Swift 目前可以”良好“的和 C、OC 交互堰燎。然而程序的世界里還有一些重要的動態(tài)語言掏父,比如 Python 、 JS秆剪,emmm赊淑,還有有實力但是不太主流的 Perl、Ruby仅讽。如果 swift 能夠愉快的的調(diào)用 Python 和 JS 的庫陶缺,那么毫無疑問會極大的拓展的 swift 的邊界。
這里需要一點想象力洁灵,因為這個設計真正的意義是@dynamicMemberLookup饱岸、 @dynamicCallable組合起來用。通過@dynamicMemberLookup動態(tài)的返回一個函數(shù)徽千,再通過@dynamicCallable來調(diào)用苫费。從語法層面來講,這種姿態(tài)下 swift 完完全全是一門動態(tài)語言双抽。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末百框,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子牍汹,更是在濱河造成了極大的恐慌琅翻,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柑贞,死亡現(xiàn)場離奇詭異,居然都是意外死亡聂抢,警方通過查閱死者的電腦和手機钧嘶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來琳疏,“玉大人有决,你說我怎么就攤上這事闸拿。” “怎么了书幕?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵新荤,是天一觀的道長。 經(jīng)常有香客問我台汇,道長苛骨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任苟呐,我火速辦了婚禮痒芝,結果婚禮上,老公的妹妹穿的比我還像新娘牵素。我一直安慰自己严衬,他們只是感情好,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布笆呆。 她就那樣靜靜地躺著请琳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪赠幕。 梳的紋絲不亂的頭發(fā)上俄精,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天,我揣著相機與錄音劣坊,去河邊找鬼嘀倒。 笑死,一個胖子當著我的面吹牛局冰,可吹牛的內(nèi)容都是我干的测蘑。 我是一名探鬼主播,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼康二,長吁一口氣:“原來是場噩夢啊……” “哼碳胳!你這毒婦竟也來了?” 一聲冷哼從身側響起沫勿,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤挨约,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后产雹,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诫惭,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年蔓挖,在試婚紗的時候發(fā)現(xiàn)自己被綠了夕土。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖怨绣,靈堂內(nèi)的尸體忽然破棺而出角溃,到底是詐尸還是另有隱情,我是刑警寧澤篮撑,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布减细,位于F島的核電站,受9級特大地震影響赢笨,放射性物質發(fā)生泄漏未蝌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一质欲、第九天 我趴在偏房一處隱蔽的房頂上張望树埠。 院中可真熱鬧,春花似錦嘶伟、人聲如沸怎憋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绊袋。三九已至,卻和暖如春铸鹰,著一層夾襖步出監(jiān)牢的瞬間癌别,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工蹋笼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留展姐,地道東北人。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓剖毯,卻偏偏與公主長得像圾笨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子逊谋,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內(nèi)容