本文系閱讀閱讀原章節後總結概括得出。由於需要我進行一定的概括提煉,如有不當之處歡迎讀者斧正。如果你對內容有任何疑問,歡迎共同交流討論。
如果你覺得這兩節的內容比較雜亂,強烈推薦我的這篇總結: 你其實真的不懂print("Hello,world")
不知道你有沒有注意到一個細節,不管你使用什麼型別的引數,print
、String.init()
函式總是可以正常工作。比如我們定義一個結構體,其中年齡作為私有屬性需要對外保密:
struct Person {
var name: String
private var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
複製程式碼
分別使用print
和String.init()
函式,檢視結果:
let kt = Person(name: "kt", age: 21)
let s: String = String(kt)
print(kt) // 輸出結果:Person(name: "kt", age: 21)
print(s) // 輸出結果:Person(name: "kt", age: 21)
複製程式碼
好訊息是這兩個函式執行正常,列印出了結構體的所有資訊,但缺點是私有屬性也被顯示出來了。如果想自定義輸出格式,或避免暴露私有成員,也很容易實現,只需要實現CustomStringConvertible
協議即可:
extension Person: CustomStringConvertible {
var description: String {
return "Name is \(name)"
}
}
複製程式碼
這樣我們就可以完全自定義輸出結果。呼叫print
或String.init()
函式將會得到Name is kt
。如果你有過Java程式設計的經驗,你會發現這和toString
函式有異曲同工之妙。
如果只是專門用於除錯,你還可以實現CustomDebugStringConvertible
協議:
extension Person: CustomStringConvertible, CustomDebugStringConvertible {
var debugDescription: String {
return "In debugging: name is \(name)"
}
}
複製程式碼
為了使用這個協議,你可以呼叫String.init(reflecting: T)
方法,這個方法得到的字串是T型別在實現CustomDebugStringConvertible
協議時定義的計算屬性debugDescription
。舉個例子說明:
let debug = String(reflecting: kt)
print(debug) // 輸出結果:In debugging: name is kt
複製程式碼
或者你也可以直接呼叫debugPrint
函式:
debugPrint(kt) // 輸出結果:In debugging: name is kt
複製程式碼
需要說明的是,即使你沒有實現CustomDebugStringConvertible
協議,也依然可以使用debugPrint
和String.init(reflecting: T)
方法,此時的字串會是CustomStringConvertible
協議中的計算屬性description
。
由於實現了CustomStringConvertible
協議的型別一般都有很好的輸出結果,你或許會實現這樣的程式碼:
func doSomethingAttractive<T: CustomStringConvertible>(with value: T) {
// 可能會呼叫print方法輸出value
}
複製程式碼
如果你這麼做了,很快你會發現String
型別並沒有實現CustomStringConvertible
協議,而字串恰好是最常被輸出的型別。這是因為Swift不希望我們以這種方式使用CustomStringConvertible
協議。我們不應該去檢查一個型別是否具有description
屬性,而是應該不管在什麼情況下都使用String.init
。我們也應該認識到,如果一個型別並不是可輸出的,呼叫print
方法確實會得到一個很醜的結果。因此,除非是一個非常簡單的類,我們總是應該實現CustomStringConvertible
協議,這用不了太多時間,但是會在以後的除錯過程中起到很大的作用,正所謂磨刀不誤砍柴工!