面向协议的日志:给 Swift 协议添加默认参数
Swift 2.2 不允许在协议声明时提供默认参数。如果你想使用协议抽象出 App 中的日志代码,就会面临一个问题。因为默认参数通常用来将源代码位置传递给日志函数。不过,你可以在协议扩展中使用默认参数,这是一个变通方案。 一个典型的日志消息应该包括日志事件的源代码位置(文件名、行号和可能的函数名)。Swift 为此提供了 func assert( @autoclosure condition: () -> Bool,@autoclosure _ message: () -> String = default,file: StaticString = #file,line: UInt = #line) 第三个和第四个参数默认扩展为调用者源代码的位置。(如果你对 一个简单、全局的日志函数你可以使用同样的方法来写一个日志函数,该函数需要一个日志消息和一个日志级别作为参数。它的接口和实现类似于: enum LogLevel: Int { case verbose = 1 case debug = 2 case info = 3 case warning = 4 case error = 5 } func log( logLevel: LogLevel,@autoclosure _ message: () -> String,line: Int = #line,function: StaticString = #function) { // 使用 `print` 打印日志 // 此时不用考虑 `logLevel` print("(logLevel) – (file):(line) – (function) – (message())") } 你可能主张使用另一种方法,而不是像这里将 message 参数声明为 具体类型为了代替全局的日志函数,我们创建一种叫做 extension LogLevel: Comparable {} func <(lhs: LogLevel,rhs: LogLevel) -> Bool { return lhs.rawValue < rhs.rawValue } struct PrintLogger { let minimumLogLevel: LogLevel func log( logLevel: LogLevel,function: StaticString = #function) { if logLevel >= minimumLogLevel { print("(logLevel) – (file):(line) – (function) – (message())") } } } 你将会这样使用 let logger = PrintLogger( minimumLogLevel: .warning) logger.log(.error,"This is an error log") // 获取日志 logger.log(.debug,"This is a debug log") // 啥也没做 带默认参数的协议下一步,我将会创建一个 protocol Logger { func log( logLevel: LogLevel,function: StaticString = #function) // 错误: 协议方法中不允许默认参数 } 因此,我不得不删掉默认参数,使协议编译能够通过。这似乎并不是一个问题。 如果你尝试使用一个 let logger2: Logger = PrintLogger(minimumLogLevel: .warning) logger2.log(.error,"An error occurred") // 错误:调用时缺少参数 logger2.log(.error,"An error occurred",file: #file,line: #line,function: #function) // 可用但是 ?
把默认参数移到协议扩展里解决方法是声明两个版本的日志函数:一,在协议声明时没有默认参数,我命名这个方法为 现在, protocol Logger { /// 打印一条日志 /// 类型必须遵循 Logger 协议的必选参数 /// - 注意:Logger 的调用者永远不应该调用此方法 /// 总是调用 log(_:,_:) 方法 func writeLogEntry( logLevel: LogLevel,file: StaticString,line: Int,function: StaticString) } extension Logger { /// Logger 协议的公开 API /// 只是调用 writeLogEntry(_:,_:,file:,line:,function:) 方法 func log( logLevel: LogLevel,function: StaticString = #function) { writeLogEntry(logLevel,message,file: file,line: line,function: function) } } 按照 session 408 的说法, 下面是采用协议后的完整 struct PrintLogger { let minimumLogLevel: LogLevel } extension PrintLogger: Logger { func writeLogEntry( logLevel: LogLevel,function: StaticString) { if logLevel >= minimumLogLevel { print("(logLevel) – (file):(line) – (function) – (message())") } } } 现在你可以像期望中那样使用协议了: let logger3: Logger = PrintLogger( minimumLogLevel: .verbose) logger3.log(.error,"An error occurred") // 撒花? 调用者的 API 可见度这个方法有一个弊端,不能简便清晰的通过访问控制给使用者指出协议中的
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |