把參數變成 closure 的 @autoclosure
@autoclosure 擁有把我們呼叫 function 時傳入的參數變成 closure 的神奇魔力,例如以下例子:
func play(_ sing: @autoclosure () -> String, at hour: Int) { if hour >= 22 { let song = sing() print("深夜適合播放\(song)") } else { print("認真寫 iOS App") }}
呼叫 play function 時,自動完成告訴我們第一個參數應該傳入字串,但剛剛我們明明將第一個參數的型別宣告為 () -> String 呀 ? 不是應該傳入 closure 嗎 ?

因為 () -> String前加了 @autoclosure的關係,Swift 會將我們傳入的參數變成 closure。我們只要傳入字串,它就能自動變成型別 () -> String 的 closure。
如以下例子,songs.randomElement()! 會成為被 { } 包起來的 closure { songs.randomElement()! }。又由於 { } 裡 songs.randomElement()! 是唯一的程式,所以 randomElement() 得到的字串會回傳,符合當初參數要求的型別 () -> String。
let songs = ["真心不騙", "已讀不回"]play(songs.randomElement()!, at: 23)
結果

但是為什麼要使用 @autoclosure 呢 ? 以剛剛的例子來說,它可以帶來以下幾個好處:
1 傳入的參數不一定會執行。
songs.randomElement()! 不一定會執行。因為白天要認真寫 App,要等到晚上 22 點之後才能開始流淚播放悲傷的情歌。
if hour >= 22 { let song = sing() print("深夜適合播放\(song)")} else { print("認真寫 iOS App")}
2 傳入的參數延後執行。
play(songs.randomElement()!, at: 23) 只是先傳入程式碼 songs.randomElement()!,要等 sing() 時才會執行。
其實我們平常開發 iOS App 時,很常使用的 assert function 就含有兩個參數是 @autoclosure 呢 !
func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)因為 @autoclosure 的關係,它的自動完成提示將如下圖, condition & message 的型別分別為 Bool 和 String,但最後其實會變成 () -> Bool 和 () -> String 型別的 closure 。

var songIndex = 5let songs = ["小幸運", "中幸運", "大幸運"]assert(songIndex < songs.count, "沒有這首歌!")

為什麼 assert 要將參數 condition & message 宣告為 @autoclosure 呢? 加了 @autoclosure,參數 condition & message 會變成 closure,不會馬上執行,於是 assert 可以檢查 build 的模式,只在 debug builds 時執行參數 condition & message 的 closure。
關於 @autoclosure,最後還有個 2 個小地方可以補充。
1 可以跟 @escaping 結合。
func play(_ sing: @escaping @autoclosure () -> String, at hour: Int) {}
2 它搭配的 function 型別必須沒有參數,因此 () -> String 可以,但 (Int) -> String 不行。
func play(_ sing: @autoclosure () -> String, at hour: Int) {}

Argument type of @autoclosure parameter must be '()'