做者:Ole Begemann,原文連接,原文日期:2016-12-07
譯者:Cwift;校對:walkingway;定稿:CMBhtml
你遇到過這個問題嗎?想要在 UI 中顯示一個可選值或將其打印到控制檯以便調試,可是你不喜歡可選值轉成字符串的默認格式:Optional(…)
或 nil
。好比:git
var someValue: Int? = 5 print("The value is \(someValue)") // → "The value is Optional(5)" someValue = nil print("The value is \(someValue)") // → "The value is nil"
當你 在字符串插值中使用可選值 的時候,實際上 Swift 3.1 會發出一個警告,由於這種行爲會產生意外的結果。這是 Julio Carrettoni、Harlan Haskins 和 Robert Widmann 提出有關 Swift 演化的論點:github
鑑於可選值永遠不適合顯示給最終的用戶,而且一般打印到控制檯中的可選值會出現使人驚訝的內容,咱們建議使用明確的方式來請求一個可選的調試描述。即:當你在字符串插值段中使用可選值時編譯器會發出警告。swift
該警告已經在最新的 Swift 開發快照(2016-12-01)中實現:api
你有幾種方式來消除這個警告:安全
添加一個顯式地轉型,把 someValue
轉換成 Int?
類型。app
使用 String(describing: someValue)
。性能
提供一個默認值,使得表達式不包含可選型,例如 someValue ?? defaultValue
。ui
多數狀況下我真的不喜歡任何一種,但這已是編譯器能夠提供的最好的方案了。第三種方案的問題是使用 空值合併操做符 ??
須要類型匹配——若是左邊的操做數是 T?
,那麼右邊的操做數必須是 T
類型的。應用到上面的例子中,這意味着我能夠提供另外一個 Int
做爲默認值,但不是一個字符串——而這種情形中字符串纔是我真正想要的。spa
爲了解決這個問題,我自定義了一個可選型的字符串合併操做符。我以爲給它取名爲 ???
,由於很明顯它與空值合併操做符有關聯。(最初我認爲重載 ??
操做符是一個好主意,即定義一個 (T?, String) -> String
類型的 ??
版本。我喜歡這種寫法,由於個人實現很好地捕捉到了空值合併操做符的意義,但這意味着放棄一些其餘上下文中的類型安全性,由於如今形如 someOptional ?? "someString
的表達式老是能夠經過編譯的。)???
操做符的左側能夠接受任意可選型,而右側接受一個默認的字符串,返回值也是字符串。若是可選值不爲空,操做符將解包並返回其字符串描述,不然返回默認值。這裏是具體實現:
infix operator ???: NilCoalescingPrecedence public func ???<T>(optional: T?, defaultValue: @autoclosure () -> String) -> String { switch optional { case let value?: return String(describing: value) case nil: return defaultValue() } }
@autoclosure
結構 確保僅當須要的時候(即當可選型爲 nil
時)才計算右操做數。這個關鍵字容許你傳遞開銷大的或具備反作用的表達式,只在極少數狀況下才須要承擔性能成本。我認爲在此用例中這種機制不是很重要,???
的定義鏡像了 標準庫中 ??
操做符的定義。(雖然我決定捨棄當前標準庫版本中的 throw /rethrows 格式。)
或者,使用 [Optional.map
]() 你能夠經過一行代碼實現該操做符,就像這樣:
public func ???<T>(optional: T?, defaultValue: @autoclosure () -> String) -> String { return optional.map { String(describing: $0) } ?? defaultValue() }
兩種實現徹底相等。你喜歡哪種版本取決於我的口味以及你的代碼風格。我不認爲哪個會顯得更清晰。
還有最後一件事我認爲須要提醒一下,你必須在 String(describing:)
(即你更喜歡基於值的描述)以及 String(reflecting:)
(更喜歡基於調試的描述)兩者之間作出選擇,以便完成值的轉換。前者更適合用於格式化須要在 UI 顯示的值,然後者可能更適合用於打印日誌。一般基於調試的描述的輸出格式長度更長,或許你能夠爲它定義另外一個運算符(例如,????)。
讓咱們使用 ???
操做符重寫本文開頭的示例:
var someValue: Int? = 5 print("The value is \(someValue ??? "unknown")") // → "The value is 5" someValue = nil print("The value is \(someValue ??? "unknown")") // → "The value is unknown"
這只是個小玩意,但我真的很喜歡它。
本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 http://swift.gg。