Log 輸出是程序開發中很重要的組成部分,雖然它並不是直接的業務代碼,但是卻可以忠實地反映我們的程序是如何工作的,以及記錄程序運行的過程中發生了什麼。
在 Swift 中,最簡單的輸出方法就是使用 print
,在我們關心的地方輸出字符串和值。但是這並不夠,試想一下當程序變得非常複雜的時候,我們可能會輸出很多內容,而想在其中尋找到我們希望的輸出其實並不容易。我們往往需要更好更精確的輸出,這包括輸出這個 log 的文件,調用的行號以及所處的方法名字等等。
我們當然可以在 print
的時候將當前的文件名字和那些必要的信息作爲參數同我們的消息一起進行打印:
// Test.swift
func method() {
//...
print("文件名:Test.swift, 方法名:method,這是一條輸出")
//...
}
但是這顯然非常麻煩,每次輸入文件名和方法名不說,隨着代碼的改變,這些 Log 的位置也可能發生改變,這時我們可能還需要不斷地去維護這些輸出,代價實在太大。
在 Swift 中,編譯器爲我們準備了幾個很有用的編譯符號,用來處理類似這樣的需求,它們分別是:
符號 | 類型 | 描述 |
---|---|---|
FILE | String | 包含這個符號的文件的路徑 |
LINE | Int | 符號出現處的行號 |
COLUMN | Int | 符號出現處的列 |
FUNCTION | String | 包含這個符號的方法名字 |
因此,我們可以通過使用這些符號來寫一個好一些的 Log 輸出方法:
func printLog<T>(message: T,
file: String = __FILE__,
method: String = __FUNCTION__,
line: Int = __LINE__)
{
print("\((file as NSString).lastPathComponent)[\(line)], \(method): \(message)")
}
這樣,在進行 log 的時候我們只需要使用這個方法就能完成文件名,行號以及方法名的輸出了。最棒的是,我們不再需要對這樣的輸出進行維護,無論在哪裏它都能正確地輸出各個參數:
// Test.swift
func method() {
//...
printLog("這是一條輸出")
//...
}
// 輸出:
// Test.swift[62], method(): 這是一條輸出
另外,對於 log 輸出更多地其實是用在程序開發和調試的過程中的,過多的輸出有可能對運行的性能造成影響。在 Release 版本中關閉掉向控制檯的輸出也是軟件開發中一種常見的做法。如果我們在開發中就注意使用了統一的 log 輸出的話,這就變得非常簡單了。使用條件編譯的方法,我們可以添加條件,並設置合適的編譯配置,使 printLog
的內容在
Release 時被去掉,從而成爲一個空方法:
func printLog<T>(message: T,
file: String = __FILE__,
method: String = __FUNCTION__,
line: Int = __LINE__)
{
#if DEBUG
print("\((file as NSString).lastPathComponent)[\(line)], \(method): \(message)")
#endif
}
新版本的 LLVM 編譯器在遇到這個空方法時,甚至會直接將這個方法整個去掉,完全不去調用它,從而實現零成本。