在 Swift 4 中为 os_log 传递可变参数

问题描述 投票:0回答:4

我正在尝试在 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 的 splatting 语法)

ios swift4 variadic
4个回答
9
投票

我还没有找到解决方案,所以做了这个愚蠢的黑客:

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)
}

4
投票

您的想法包含几个问题:

  1. Apple 不鼓励将

    os_log
    包装在另一个函数中,这样做会导致失去统一日志系统的一些不错的功能,例如自动在日志中包含代码行、库、文件等。

  2. 一旦将

    args
    传递给您自己的函数,该函数类型从 cvargs 传递到 [String] 并且理论上不可能重新构建参数列表,您可以在此处的答案中找到令人惊奇的解释:https: //stackoverflow.com/a/50942917/465916


3
投票

这就是我用来包装 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 以及私有和公共日志记录的好处的更多信息,请查看此链接:

https://www.testdevlab.com/blog/2018/04/how-to-create-categorize-and-filter-ios-logs/


1
投票

对此做了更多研究。结果发现

os_log
实际上是一个 C 宏。这在如何映射到 Swifts 可变参数方面产生了各种各样的问题。

但是,该宏还捕获其他调试信息,并且无论如何都可能不安全。

© www.soinside.com 2019 - 2024. All rights reserved.