第七章——字符串(字符串调试)

325 阅读3分钟

本文系阅读阅读原章节后总结概括得出。由于需要我进行一定的概括提炼,如有不当之处欢迎读者斧正。如果你对内容有任何疑问,欢迎共同交流讨论。

如果你觉得这两节的内容比较杂乱,强烈推荐我的这篇总结: 你其实真的不懂print("Hello,world")

不知道你有没有注意到一个细节,不管你使用什么类型的参数,printString.init()函数总是可以正常工作。比如我们定义一个结构体,其中年龄作为私有属性需要对外保密:

struct Person {
var name: String
private var age: Int

init(name: String, age: Int) {
self.name = name
self.age = age
}
}

分别使用printString.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)"
}
}

这样我们就可以完全自定义输出结果。调用printString.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协议,也依然可以使用debugPrintString.init(reflecting: T)方法,此时的字符串会是CustomStringConvertible协议中的计算属性description

由于实现了CustomStringConvertible协议的类型一般都有很好的输出结果,你或许会实现这样的代码:

func doSomethingAttractive<T: CustomStringConvertible>(with value: T) {
// 可能会调用print方法输出value
}

如果你这么做了,很快你会发现String类型并没有实现CustomStringConvertible协议,而字符串恰好是最常被输出的类型。这是因为Swift不希望我们以这种方式使用CustomStringConvertible协议。我们不应该去检查一个类型是否具有description属性,而是应该不管在什么情况下都使用String.init。我们也应该认识到,如果一个类型并不是可输出的,调用print方法确实会得到一个很丑的结果。因此,除非是一个非常简单的类,我们总是应该实现CustomStringConvertible协议,这用不了太多时间,但是会在以后的调试过程中起到很大的作用,正所谓磨刀不误砍柴工!