如何最好地在Swift中记录失败的演员表?

时间:2020-04-16 19:30:30

标签: swift

我如何以优雅的方式记录铸造失败?理想情况下,我很乐意做一些类似的事情,但是由于不可能扩展Any,所以我很茫然。我还不知道使用分配的类型(例如String.self)进行转换的方法,所以有两个问题。

let actuallyADouble: Any = 3.0
let stringType = String.self
guard let anyToString = actuallyADouble as? stringType else {
    actuallyADouble.logFailedCast(to: stringType)
    return
}
setString(anyToString) // will never reach

对于奖励积分,魔术可能是扩展所有投射的一种方式,因此我可以执行以下操作,并且在失败范围之外会出现日志:

guard let anyToString = actuallyADouble as? stringType else { return }

背景

我们有一个Swift项目,该项目没有实施避免强制铸造的做法,所以有很多类似这样的事情:

let anyToString = actuallyADouble as! String

我们正在重构此更安全的代码:

guard let anyToString = actuallyADouble as? String else { return }

现在我们的应用程序不会崩溃,但是我们将继续操作,而不会出现任何错误,这可能会使故障排除更加困难。

3 个答案:

答案 0 :(得分:1)

您可以创建一个免费功能,例如:

func loggedCast<A, B>(_ input: A, to: B.Type) -> B? {
    if let casted = input as? B {
        return casted
    }
    else {
        // Log however you want
        print("Failed to cast \(String(describing: input)) to type \(B.self)")
        return nil
    }
}

您可以像这样使用:

let actuallyADouble: Any = 3.0
guard let anyToString = loggedCast(actuallyADouble, to: String.self) else { return }

不幸的是,您无法摆脱guard let ___ = ... else { return }样板,因为控制流是显式的,并且您无法使调用者函数从被调用者函数返回。

答案 1 :(得分:1)

正如您所说,扩展Any是不可能的。但是可选内容可以容纳任何内容,如果您依赖它们,则可以获取日志记录,并拥有与已使用的代码非常相似的代码。

extension Optional {
  struct UnwrapError: Error { }

  static func unwrap(_ any: Any) throws -> Wrapped {
    guard let wrapped = any as? Wrapped else {
      print("\(any) cannot be cast to \(Wrapped.self)")
      throw UnwrapError()
    }

    return wrapped
  }
}
try Double?.unwrap(actuallyADouble) // 3
let double: Double = try Optional.unwrap(actuallyADouble) // 3
try? String?.unwrap(actuallyADouble) // nil. "3.0 cannot be cast to String"

…甚至只是

extension Optional {
  init(_ any: Any) {
    self = any as? Wrapped
    if self == nil {
      print("\(any) cannot be cast to \(Wrapped.self)")
    }
  }
}
Double?(actuallyADouble) // 3
String?(actuallyADouble) // nil. "3.0 cannot be cast to String"

答案 2 :(得分:0)

这是我自己想到的。绝对可以,但是我觉得有更好的方法:

func DLogFailedCast<T>(object: T, desiredType: Any.Type) {
    let message = "CAST FAILURE: \(type(of: object)) to \(desiredType)"
    let log = OSLog(subsystem: "com.company.example", category: "Failed Cast")
    os_log("%@", log: log, type: .debug, message)
}

用法:

let actuallyADouble: Any = 3.0
guard let anyToString = actuallyADouble as? String else {
    DLogFailedCast(object: actuallyADouble, desiredType: String.self)
    return
}
setString(anyToString)
相关问题