【问题标题】:Passing variadic args in Swift 4 for os_log在 Swift 4 中为 os_log 传递可变参数
【发布时间】:2017-09-29 23:34:58
【问题描述】:

我正在尝试在 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 API 的情况下,您可能正在寻找 withVaList?
  • 尝试使用VaList 失败:-( 供参考,os_log 签名为func os_log(_ message: StaticString, dso: UnsafeRawPointer? = #dsohandle, log: OSLog = default, type: OSLogType = default, _ args: CVarArg...)
  • 将可变参数传递给另一个可变参数调用没有神奇的语法。
  • 我不确定您是否可以将数组传递给 ... 参数。在任何情况下,您的函数的 arg 类型都需要匹配(即使用 CVarArg 而不是 Any)
  • 经过几个小时的搜索,我怀疑@KevinBallard 是正确的。不过,我仍然希望有人在星期一之前为我提供一些神奇的语法,这样我就可以继续我的下一个任务了。

标签: ios swift4 variadic


【解决方案1】:

您的想法包含几个问题:

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

  2. 一旦args 被传递给您自己的函数,该类型从 cvargs 传递到 [String] 并且理论上不可能重新构建 args 列表,您可以在第一个答案中找到惊人的解释这里:Why does wrapping os_log() cause doubles to not be logged correctly?

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案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/

      【讨论】:

      • 您认为这是可行的解决方案吗?我认为这不能与一个以上的可变参数一起使用。
      【解决方案4】:

      对此进行了更多研究。原来os_log 实际上是一个 C 宏。这给它如何映射到 Swifts 可变参数 args 带来了各种各样的问题。

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

      【讨论】:

      • 在 Swift 中,os_log 是在 Swift 框架覆盖层中实现的函数。它的工作方式与 C 宏非常不同。在 C 中,对 os_log 的调用实际上在编译时解析了格式字符串。在 Swift 中,它是在运行时解析的(在框架覆盖中)。这不仅意味着解析不同,因此行为可能不同(例如,在 Swift 3.0 中,它没有正确尊重 public 修饰符),而且还意味着它更慢。
      • 好消息。我还是 Swift 的新手,所以这些低级问题可能相当神秘和令人沮丧。我假设它是对 C 宏的直接映射,但如果它在运行时解析,显然情况并非如此。
      猜你喜欢
      • 1970-01-01
      • 2018-07-21
      • 1970-01-01
      • 1970-01-01
      • 2012-12-29
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      • 2015-10-21
      相关资源
      最近更新 更多