在 Swift 4 中为 os_log 传递可变参数
Passing variadic args in Swift 4 for os_log
我正尝试在 Swift 4 / iOS 11 中为 os_log 编写一个方便的包装器,但我已经 运行 通过可变参数。
基本上,我想编写一个如下所示的函数。
static let logger = OSLog(subsystem: "com.example.foo", category: "foobar")
func logError(_ message: StaticString, _ args: Any...) {
os_log(message, log: logger, type: .error, args)
}
不幸的是,我似乎无法弄清楚传递参数的神奇语法,并且在 CVarArg 讨论的泥潭中有点迷失了方向。
(...这让我想念 Python 的拼写语法)
对此进行了更多研究。原来 os_log
实际上是一个 C 宏。这导致了它如何映射到 Swifts 可变参数的各种问题。
但是,该宏还会捕获其他调试信息,无论如何都可能不安全。
我还没有找到解决方案,所以做了这个愚蠢的 hack:
switch args.count {
case 0:
os_log(message, log: log!, type: type)
case 1:
os_log(message, log: log!, type: type, args[0])
case 2:
os_log(message, log: log!, type: type, args[0], args[1])
case 3:
os_log(message, log: log!, type: type, args[0], args[1], args[2])
default:
os_log(message, log: log!, type: type, args)
}
这是我用来包装的 os_log:
import Foundation
import os.log
protocol LogServicing: class {
func debug(_ message: StaticString, _ args: CVarArg...)
func info(_ message: StaticString, _ args: CVarArg...)
func error(_ message: StaticString, _ args: CVarArg...)
}
enum LogType {
case debug
case info
case error
case fault
}
class LogService: LogServicing {
private var osLog: OSLog?
let subsystem: String
let category: String
init(subsystem: String = Bundle.main.bundleIdentifier ?? "", category: String = "") {
if #available(iOS 10.0, *) {
let osLog = OSLog(subsystem: subsystem, category: category)
self.osLog = osLog
}
self.subsystem = subsystem
self.category = category
}
func log(type: LogType, message: StaticString) {
log(type: type, message: message, "")
}
func log(type: LogType, message: StaticString, _ args: CVarArg...) {
if #available(iOS 10.0, *) {
guard let osLog = osLog else { return }
let logType: OSLogType
switch type {
case .debug:
logType = .debug
case .error:
logType = .error
case .fault:
logType = .fault
case .info:
logType = .info
}
os_log(message, log: osLog, type: logType, args)
print(message, args)
} else {
NSLog(message.description, args)
}
}
func debug(_ message: StaticString, _ args: CVarArg...) {
log(type: .debug, message: message, args)
}
func info(_ message: StaticString, _ args: CVarArg...) {
log(type: .info, message: message, args)
}
func error(_ message: StaticString, _ args: CVarArg...) {
log(type: .error, message: message, args)
}
}
我是这样创建的:
self.logService = LogService(subsystem: "com.softbolt.app", category: "network")
并像这样使用它:
self.logService.info("HttpResponse %{public}@", url)
如果您想了解更多关于 os_log 以及私有和 public 日志记录的好处,请查看此 link:
https://www.testdevlab.com/blog/2018/04/how-to-create-categorize-and-filter-ios-logs/
您的想法包含几个问题:
Apple 不鼓励在另一个函数中包装 os_log
,这样做会导致失去统一日志系统的一些很好的功能,比如在自动记录。
一旦 args
被传递给你自己的函数,类型从 cvargs 传递给 [String] 并且理论上不可能 re-build args 列表,你可以在第一个答案中找到一个惊人的解释:
我正尝试在 Swift 4 / iOS 11 中为 os_log 编写一个方便的包装器,但我已经 运行 通过可变参数。
基本上,我想编写一个如下所示的函数。
static let logger = OSLog(subsystem: "com.example.foo", category: "foobar")
func logError(_ message: StaticString, _ args: Any...) {
os_log(message, log: logger, type: .error, args)
}
不幸的是,我似乎无法弄清楚传递参数的神奇语法,并且在 CVarArg 讨论的泥潭中有点迷失了方向。
(...这让我想念 Python 的拼写语法)
对此进行了更多研究。原来 os_log
实际上是一个 C 宏。这导致了它如何映射到 Swifts 可变参数的各种问题。
但是,该宏还会捕获其他调试信息,无论如何都可能不安全。
我还没有找到解决方案,所以做了这个愚蠢的 hack:
switch args.count {
case 0:
os_log(message, log: log!, type: type)
case 1:
os_log(message, log: log!, type: type, args[0])
case 2:
os_log(message, log: log!, type: type, args[0], args[1])
case 3:
os_log(message, log: log!, type: type, args[0], args[1], args[2])
default:
os_log(message, log: log!, type: type, args)
}
这是我用来包装的 os_log:
import Foundation
import os.log
protocol LogServicing: class {
func debug(_ message: StaticString, _ args: CVarArg...)
func info(_ message: StaticString, _ args: CVarArg...)
func error(_ message: StaticString, _ args: CVarArg...)
}
enum LogType {
case debug
case info
case error
case fault
}
class LogService: LogServicing {
private var osLog: OSLog?
let subsystem: String
let category: String
init(subsystem: String = Bundle.main.bundleIdentifier ?? "", category: String = "") {
if #available(iOS 10.0, *) {
let osLog = OSLog(subsystem: subsystem, category: category)
self.osLog = osLog
}
self.subsystem = subsystem
self.category = category
}
func log(type: LogType, message: StaticString) {
log(type: type, message: message, "")
}
func log(type: LogType, message: StaticString, _ args: CVarArg...) {
if #available(iOS 10.0, *) {
guard let osLog = osLog else { return }
let logType: OSLogType
switch type {
case .debug:
logType = .debug
case .error:
logType = .error
case .fault:
logType = .fault
case .info:
logType = .info
}
os_log(message, log: osLog, type: logType, args)
print(message, args)
} else {
NSLog(message.description, args)
}
}
func debug(_ message: StaticString, _ args: CVarArg...) {
log(type: .debug, message: message, args)
}
func info(_ message: StaticString, _ args: CVarArg...) {
log(type: .info, message: message, args)
}
func error(_ message: StaticString, _ args: CVarArg...) {
log(type: .error, message: message, args)
}
}
我是这样创建的:
self.logService = LogService(subsystem: "com.softbolt.app", category: "network")
并像这样使用它:
self.logService.info("HttpResponse %{public}@", url)
如果您想了解更多关于 os_log 以及私有和 public 日志记录的好处,请查看此 link:
https://www.testdevlab.com/blog/2018/04/how-to-create-categorize-and-filter-ios-logs/
您的想法包含几个问题:
Apple 不鼓励在另一个函数中包装
os_log
,这样做会导致失去统一日志系统的一些很好的功能,比如在自动记录。一旦
args
被传递给你自己的函数,类型从 cvargs 传递给 [String] 并且理论上不可能 re-build args 列表,你可以在第一个答案中找到一个惊人的解释: