面向協議的日誌 給 Swift 協議新增預設引數

2021-09-18 02:29:13 字數 3017 閱讀 7875

乙個典型的日誌訊息應該包括日誌事件的源**位置(檔名、行號和可能的函式名)。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擴充套件的使用還能提供一些方法的預設實現,而且不僅類可以遵循協議,現在的列舉和結構體也能遵循協議...