【问题标题】:Any? incorect semantics任何?不正确的语义
【发布时间】:2019-11-27 13:00:31
【问题描述】:

我在 swift 中玩了一些代码,遇到了一个有趣的案例。 让我们从一个小序言开始:假设您创建了一些可选变量:

let a: String? = "abcd"; let b: Int? = 4
print(
    "Type of \(a) is \(type(of: a))" //Type of Optional("abcd") is Optional<String>
    "Type of \(b) is \(type(of: b))", //Type of Optional(4) is Optional<Int>
    separator: "\n"
)

然后你强制解包,所以 aubu 的类型不是可选的。

let au = a!; let bu = b!
print(
    "Type of \(au) is \(type(of: au))", //Type of abcd is String
    "Type of \(bu) is \(type(of: bu))", //Type of 4 is Int

    au + String(bu), //abcd4
    separator: "\n"
)

看起来很合理,但是当您尝试将相同的代码应用于 Optional&lt;Any&gt; 时,事情开始变得很奇怪:

let a: Any? = "abcd"; let b: Any? = 4
let au = a!; let bu = b!
print(
    "Type of \(a) is \(type(of: a))", //Type of Optional("abcd") is Optional<Any>
    "Type of \(b) is \(type(of: b))", //Type of Optional(4) is Optional<Any>
    "Type of \(au) is \(type(of: au))", //Type of abcd is String
    "Type of \(bu) is \(type(of: bu))", //Type of 4 is Int
    //au + String(bu),
    separator: "\n"
)

但是现在如果你尝试做同样的连接au + String(bu),swift 将产生编译错误,即使这两个变量已知是某种具体类型,正如 swift 本身所报告的那样。 错误是:

error: protocol type 'Any' cannot conform to 'LosslessStringConvertible' because only concrete types can conform to protocols

这肯定看起来像一个错误,不是吗。请分享您的意见。

【问题讨论】:

  • 请注意,您没有将任何变量从Any? 转换为StringInt,只是将它们解包到Any。调用type(of:) 的结果仍然很奇怪,希望有人能解释一下。

标签: swift any forceunwrap swift-optionals


【解决方案1】:

正如其他人所指出的,type(of:) 是值的动态运行时类型。编译器仅依赖于静态的、可证明的值类型。在上面的代码中,编译器唯一可以证明的是au 的类型是Any。符合 Any 的众多类型之一是 String,但编译器并不知道在所有可能的代码路径中,值真的是 String。

由于没有func + (Any, String)重载,所以无法编译。

请注意,Any? 是一种奇怪而危险的类型。它的问题不是这个示例的原因,但它与 Optional 提升交互的方式可能会导致严重的歧义和混乱。 Swift 中的每种类型都可以隐式提升为该类型的 Optional。 Swift 中的每种类型都可以隐式转换为 Any。结合这两个事实意味着Any可以隐式提升为Any?,或Any??,或Any???等,但所有这些也是Any的子类型。这会造成各种微妙的头痛。你应该强烈避免使用Any;它很少是正确的工具。但是您应该更加小心地允许Any? 出现在您的代码中。这表明可能出现了问题。

【讨论】:

  • 感谢您的澄清。那么这是否意味着 swift 具有动态性或一些可访问的设施来在运行时观察类型的元数据?特别是,我想创建一个运行时函数来检查所有一致性和约束并传播可以处理的错误,而不是仅仅破坏程序。
  • 我不相信您正在寻找的东西是可能的,但通常不需要它,也不应该让程序崩溃。通常类型应该在编译时就知道,所以如果你犯了错误,它就不会编译。无需在运行时检查。而且应该不可能崩溃。如果您有遇到问题的具体案例,请提出一个包含详细信息的问题,我希望有一个很好的答案。
【解决方案2】:

type(of: T) 方法获取任何变量的运行时类型。这就是为什么您将 (type(of: au) 视为 String 的原因。但出于安全原因,Swift 不允许隐式类型转换。这就是您不能在 Swift 中添加 Int 和 Double 而不进行转换的原因。您需要显式转换你的代码可以工作。

【讨论】:

    【解决方案3】:

    type(of: T) 返回值的动态类型。

    您可以使用type(of:) 函数来查找值的动态类型, 特别是当动态类型与静态类型不同时。这 值的静态类型 是该值的已知编译时类型。这 值的动态类型 是该值在运行时的实际类型,即 可以是其具体类型的子类型。

    这个解释来自type(of: T)函数上面的评论。在 Xcode 中 Cmd+点击type(of: T) 阅读更多内容

    【讨论】:

      猜你喜欢
      • 2019-06-02
      • 2010-10-28
      • 2013-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-21
      • 2016-02-25
      • 1970-01-01
      相关资源
      最近更新 更多