Swift:漂亮的 print() Pt.2

做者:Andyy Hope,原文連接,原文日期:2016-04-14
譯者:SketchK;校對:Crystal Sun;定稿:CMBgit

時尚日誌,由你作主程序員

以前的文章中,咱們討論了在輸出日誌中使用 emojis 的好處,它能夠幫助咱們更好的去消化和吸取大量的信息,不過我提供的實現方式並不怎麼樣,沒有足夠多的例子供你將其應用在本身的代碼中。github

我將遵照以前的約定繼續討論這個話題,向你展現如何使用 emojis 來實現輸出日誌的功能,只需在 print 函數上再多花費一點兒工夫。swift

節省成本

在接下來的文章中,我會打破 Swift 的命名規範,這樣作我可不缺理由。爲了下降新方案的成本, 要在儘量減小鍵盤敲擊次數的狀況下達到一樣的目標,好比字母大小寫和標題大小寫的問題。無論怎麼樣,若是看到文章的最後,你還在爲一些細節而糾結的話, 你絕對應該把它們改爲你想要的樣子。xcode

介紹 log

enum log { }

這裏使用枚舉類型代替類或結構的緣由不少。緣由之一是,咱們永遠不須要實例化一個日誌。選擇枚舉而不是函數,是想確保實現一個安全的日誌輸出方案。不用着急,一會你就會明白我所說的「安全」的含義了.安全

枚舉成員與值關聯

enum log {
    case ln(_ line: String)
    case url(_ url: String)
    case obj(_ any: AnyObject)
}

可能有些人還不知道 ln(line) 曾經在 swift 語言中出現過。 print() 在 Swift 2.0 以後替代了 println() , 且主要用於日誌輸出。我在這裏舉了一些例子來解釋 log 枚舉的可擴展性。app

要先爲每個枚舉值設置關聯值,畢竟得現有東西才能輸出日誌吧?請注意,這裏忽略了參數標籤,由於已經使用參數名稱來描述函數的參數了。框架

看一下目前的狀況吧:函數

print(log.ln(「Hello World」))
// ln("Hello World")

print("Hello World")
// "Hello World"

嗯,看樣子彷佛是完成了。但這看起來並非一個能夠替代 print 的方案。主要緣由有這些:工具

  • 仍是要敲擊不少次鍵盤

  • 除了原始信息外還有許多沒必要要的內容

  • 外表不怎麼樣

  • 沒有一個 emojis

  • 千言萬語,就一句:「這方案太糟糕了」

如今須要完善上面的五個問題, 以便實現以前定下的目標.

自定義運算符

postfix operator / { }

先假定大家大多數人在這以前都沒有遇到過自定義運算符的需求。不要緊,我也是最近才用上這個功能, 不過用的也不是太多.

要建立一個 postfix 後置運算符,展現的內容會出如今運算符的左側,想讓它出如今日誌代碼的後面, 只用敲擊一次鍵盤就能實現。

選擇 / 符號是由於它最接近註釋符號但不會真正產生註釋,另外它也是少數幾個不用 shift 鍵來就能夠直接打出來字符。

...感受本身就像是政客,在不停的想辦法減小實現預算。

實現

postfix func / (target: log) { 
    switch target {
    case ln(let line):
        log("✏️", line)
    case url(let url):
        log("?", url)
    case obj(let object):
        log("?", object)
}

這段代碼看起來像聲明,有代碼主體(body), 增長了在約束,確保自定義操做符接收的參數是 log 枚舉值,這在必定程度上符合我所說的的「安全代碼」。「安全代碼」這個概念還體如今使用枚舉替代類或結構體,由於枚舉中的 switch 必定會把全部狀況都檢查一遍。這樣,每次想在輸出日誌中添加一個新的 emoji 時, 也必須將其添加到操做符中的 switch 語句裏。

private func log<T>(emoji: String, _ object: T) {
    print(emoji + 「 「 + String(object))
}

終於獲得了這個看似很是簡單 log 函數。它是一個私有函數,可不被 .swift 文件以外的任何東西訪問到,另外它的第二個參數是一個泛型,這個參數可以接受任意類型的值。

正如你所看到的同樣,這是一個至關簡單的打印語句,把 emoji 表情和對象用空格連起來。

用起來

log.ln(「Pretty」)/
✏️ Pretty
log.url(url)/
? http://www.andyyhope.com
log.obj(date)/
? 2016–04–02 23:23:05 +0000
Maybe i should use a screenshot here instead?

如今咱們有一個能夠替代系統原有輸出日誌的方案了, 新方案只須要進行兩次敲擊,相比其餘的日誌輸出工具,脫穎而出、暢快淋漓。不過這還沒完呢...

性能提高

大多數的程序員都會有這麼一個共識,就是調用 print 方法會下降 app 的性能。若是含有 print 的代碼散落在程序的不一樣地方, 在 debug 的時候仍是能夠接受, 但是要把 app 上傳到 AppStore 時,最好仍是移除這些內容。

"你是在說,每一次我都必須在提交 App Store 前註釋掉全部的 print 語句, 而後再在 debug 的時候把它們恢復回來麼?" —— 你

預編譯標識符

能夠用 Xcode 給工程建立配置文件, 在默認狀況下 Xcode 會爲每一個工程提供兩個配置文件 : Debug 和 Release.

在使用模擬器或用 USB 鏈接真機時,默認模式是 debug,用手機打開從 AppStore 下載的 app 時,默認模式是 relsase。

把剛纔寫好的代碼放入到用於標識 debug 狀態的預編譯標識符裏, 這樣就不用在每次打包的時候對這些代碼進行註釋/恢復/增長/刪除等操做。至關於告訴編譯器:「Hi,哥們,除了 release 模式下, 你都得運行這段代碼!」

Build Settings

  1. 點擊 Project Navigator 圖標

  2. 點擊 Project 名稱

  3. 點擊 Build Settings 按鈕

  4. 搜索 「Compiler Flag」

  5. 展開 「Other C Flags」

  6. 點擊 「+」 按鈕

  7. 輸入 「-D DEBUG」

最終把整個 print 函數放到預編譯的標識符中。

private func log<T>(emoji: String, _ object: T) {
    #if DEBUG
        print(emoji + 「 「 + String(object))
    #endif
}

哦了!如今只有在開發狀態下 print 纔會生效。把 build configuration scheme 設置改成Release,運行 app,這樣就能夠檢測以前的操做是否成功,固然別忘了檢測完把狀態切回到 Debug 模式。

作一個支持 Carthage 或 Cocoapod 的框架

讀到這, 也許會想:「這些個點子太好了, 若是 Andyy 將這些功能弄成一個...」,但事實是這樣作並很差。

緣由就是, 假如我提供了上面三種方式中的任意一種, 那麼每當你想利用它作日誌輸出的時候,你就必須在 Swift 文件裏引入這個庫, 這樣作實在太傻了, 還須要作一些額外的操做來管理它們。這也是爲何許多 NSLog 的替代品在 Objective-C 中表現很差的緣由。

import Log // 這看起來像坨 ?

探索和把玩

我專門提供了一個 playground 文件,可以讓你測試一下今天所讀到的所有內容,若是想使用這個文件,只須要將 log.swift 文件拖入到工程中便可。另外在示例代碼中會有一些額外的枚舉值, 或許你會發現這些額外的例子對你的平常工做頗有用, 若是是這樣的話,拿走不謝!


示例代碼能夠在 Github 上下載到.

就像以前同樣, 若是你喜歡今天讀到的內容或者親手實現了它,請記得發我一個 tweet。很渴望聽到大家的聲音, 讓我很受用.

本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 http://swift.gghttp://swift.gg

相關文章
相關標籤/搜索