乙個典型的日誌訊息應該包括日誌事件的源**位置(檔名、行號和可能的函式名)。swift 為此提供了#file
,#line
,#column
和#function
func assert(
@autoclosure condition: () -> bool,
@autoclosure _ message: () -> string = default,
file: staticstring = #file,
line: uint = #line)
第三個和第四個引數預設擴充套件為呼叫者源**的位置。(如果你對@autoclosure
屬性有疑問,它把乙個表示式封裝為乙個閉包,有效地將表示式的執行從呼叫處延遲到函式體執行時,即閉包表示式在明確使用時才會執行。assert
只在除錯構建時使用它來執行 condition 引數的計算(可能代價高昂或者有***),同時只在斷言失敗時才計算 message 引數。)
你可以使用同樣的方法來寫乙個日誌函式,該函式需要乙個日誌訊息和乙個日誌級別作為引數。它的介面和實現類似於:
enum loglevel: int
func log(
loglevel: loglevel,
@autoclosure _ message: () -> string,
file: staticstring = #file,
line: int = #line,
function: staticstring = #function)
你可能主張使用另一種方法,而不是像這裡將 message 引數宣告為@autoclosure
。這個屬性並沒有提供多少好處,因為 message 引數無論什麼情況都會計算。既然如此,我們來修改一下。
為了代替全域性的日誌函式,我們建立一種叫做printlogger
的型別,它用最小日誌級別初始化,只會記錄最小日誌級別的事件。loglevel
因此需要comparable
協議,這是為什麼我之前把它宣告為int
型來儲存原始資料的原因:
extension loglevel: comparable {}
func <(lhs: loglevel, rhs: loglevel) -> bool
struct printlogger
}}
你將會這樣使用printlogger
:
let logger = printlogger(
minimumloglevel: .warning)
logger.log(.error, "this is an error log")
// 獲取日誌
logger.log(.debug, "this is a debug log")
// 啥也沒做
下一步,我將會建立乙個logger
協議作為printlogger
的抽象。它將允許我今後使用更高階的實現替換簡單的 print 語句,比如記錄日誌到檔案或者傳送日誌給伺服器。但是,我在這裡碰了壁,因為 swift 不允許在協議宣告時提供預設引數。下面的**無法通過編譯:
protocol logger
因此,我不得不刪掉預設引數,使協議編譯能夠通過。這似乎並不是乙個問題。printlogger
可以使用帶有空擴充套件的協議,它目前的實現基本上能滿足要求。通過使用乙個logger: printlogger
型別的變數和之前的用法沒有什麼區別。
如果你嘗試使用乙個logger2: logger
協議型別的變數,問題馬上就來了,因為你呼叫**時是猜不到具體的實現的:
let logger2: logger = printlogger(minimumloglevel: .warning)
logger2.log(.error, "an error occurred")
// 錯誤:呼叫時缺少引數
logger2.log(.error, "an error occurred", file: #file, line: #line, function: #function)
// 可用但是 ?
logger2
只知道這個日誌函式有五個必須的引數,所以你不得不每次都全部寫上它們。討厭!
解決方法是宣告兩個版本的日誌函式:一,在協議宣告時沒有預設引數,我命名這個方法為writelogentry
。二,在logger
的協議擴充套件裡包含預設引數(這是允許的),我保持這個方法名就為log
,因為該方法會是這個協議的公開介面。
現在,log
的實現只有一行**:呼叫writelogentry
,傳入所有引數,而呼叫者通過預設引數傳入了源**位置。writelogentry
從另一方面來說是協議必須實現的介面卡方法,用來執行實際的日誌操作。這裡是完整的協議**:
protocol logger
extension logger
}
下面是採用協議後的完整printlogger
型別:
struct printlogger
extension printlogger: logger
}}
現在你可以像期望中那樣使用協議了:
let logger3: logger = printlogger(
minimumloglevel: .verbose)
logger3.log(.error, "an error occurred") // 撒花?
Swift 面向協議程式設計之協議擴充套件
協議的命名遵循swift的標準庫,即協議名以 type able ible 結尾。例如 sequencetype,generatortype,customstringcoveeertible,type定義行為,able定義元素怎樣做事。swift 能擴充套件協議 協議可以新增方法和屬性 協議擴充套件...
Swift 面向協議程式設計入門
本文講的是swift 面向協議程式設計入門,class humanclass var classyhuman humanclass name bob classyhuman.name bob var newclassyhuman classyhuman created a copied object...
Swift面向協議程式設計入門指北
熟悉objective c語言的同學們肯定對協議都不陌生,在swift中蘋果將protocol這種語法發揚的更加深入和徹底。swift中的protocol不僅能定義方法還能定義屬性,配合extension擴充套件的使用還能提供一些方法的預設實現,而且不僅類可以遵循協議,現在的列舉和結構體也能遵循協議...