Swift:漂亮的 print() Pt.2
時尚日志,由你做主
在 之前的文章 中,我們討論了在輸出日志中使用 emojis 的好處,它可以幫助我們更好的去消化和吸收大量的信息,不過我提供的實現方式并不怎么樣,沒有足夠多的例子供你將其應用在自己的代碼中。
我將遵守之前的約定繼續討論這個話題,向你展示如何使用 emojis 來實現輸出日志的功能,只需在 print 函數上再多花費一點兒工夫。
節省成本
在接下來的文章中,我會打破 Swift 的命名規范,這樣做我可不缺理由。為了降低新方案的成本, 要在盡可能減少鍵盤敲擊次數的情況下達到同樣的目標,比如字母大小寫和標題大小寫的問題。不管怎么樣,如果看到文章的最后,你還在為一些細節而糾結的話, 你絕對應該把它們改成你想要的樣子。
介紹 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 枚舉的可擴展性。
要先為每一個枚舉值設置關聯值,畢竟得現有東西才能輸出日志吧?請注意,這里忽略了參數標簽,因為已經使用參數名稱來描述函數的參數了。
看一下目前的情況吧:
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(":pencil2:", line)
case url(let url):
log(":earth_asia:", url)
case obj(let object):
log(":small_blue_diamond:", object)
}
這段代碼看起來像聲明,有代碼主體(body), 增加了在約束,確保自定義操作符接收的參數是 log 枚舉值,這在一定程度上符合我所說的的“安全代碼”。“安全代碼”這個概念還體現在使用枚舉替代類或結構體,因為枚舉中的 switch 一定會把所有情況都檢查一遍。這樣,每次想在輸出日志中添加一個新的 emoji 時, 也必須將其添加到操作符中的 switch 語句里。
private func log<T>(emoji: String, _ object: T) {
print(emoji + “ “ + String(object))
}
終于得到了這個看似非常簡單 log 函數。它是一個私有函數,可不被 .swift 文件之外的任何東西訪問到,另外它的第二個參數是一個泛型,這個參數能夠接受任意類型的值。
正如你所看到的一樣,這是一個相當簡單的打印語句,把 emoji 表情和對象用空格連起來。
用起來
log.ln(“Pretty”)/
:pencil2: Pretty
log.url(url)/
:earth_asia: http://www.andyyhope.com
log.obj(date)/
:small_blue_diamond: 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
- 點擊 Project Navigator 圖標
- 點擊 Project 名稱
- 點擊 Build Settings 按鈕
- 搜索 “Compiler Flag”
- 展開 “Other C Flags”
- 點擊 “+” 按鈕
- 輸入 “-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 // 這看起來像坨 :hankey:
探索和把玩
我專門提供了一個 playground 文件,能夠讓你測試一下今天所讀到的全部內容,如果想使用這個文件,只需要將 log.swift 文件拖入到工程中即可。另外在示例代碼中會有一些額外的枚舉值, 或許你會發現這些額外的例子對你的日常工作很有用, 如果是這樣的話,拿走不謝!
示例代碼 可以在 Github 上下載到.
就像之前一樣, 如果你喜歡今天讀到的內容或者親手實現了它,請記得發我一個 tweet 。很渴望聽到你們的聲音, 讓我很受用.
來自:http://swift.gg/2017/02/21/swift-pretty-in-print-pt-2/