【问题标题】:About variable type in Swift关于 Swift 中的变量类型
【发布时间】:2016-04-04 18:43:48
【问题描述】:

我了解String! 类型和String? 类型之间的区别。但是String 类型呢?它与 swift 中的 String!String? 有何不同? String! 类型是否与String 类型相同?

说,我有这样的课:

class Person {
  private var _name: String!

  var name: String {
      return _name
  }

  init(name: String) {
     _name = name
  }
}

没有编译器错误,看起来String 类型与String! 类型相同。但我不确定...

【问题讨论】:

    标签: swift swift2 swift2.2


    【解决方案1】:

    StringString! 不相同。语言中恰好有足够的糖在它们之间进行转换。同样,语言中有糖可以在StringString? 之间进行转换(但不能反过来)。

    从基础开始。有String。除非有一些强有力的理由,否则当您指的是字符串时,您应该使用String。其他一切都是“更多的东西”,除非你需要,否则你不应该添加它。

    Optional<String>。这只是一个有两种情况的枚举,一种有值,一种没有值:

    public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {
        case None
        case Some(Wrapped)
        // ...
    }
    

    Optional 有一个后缀运算符 !,如果可用则返回 Wrapped,如果不可用则崩溃。到目前为止,还没有魔法。这是您可以自己构建的东西。

    Optional 周围很少有魔法。首先,类型Wrapped? 被编译器神奇地转换为Optional&lt;Wrapped&gt;(对于任何Wrapped)。这只是语法糖。这两个符号是相同的。其次,?.“运算符”存在可选链接(它不是真正的运算符;它是语言的一部分,你不能自己构建它)。然后是可选的促销。如果需要,任何类型的Wrapped 都可以自动隐式转换为Wrapped?Optional 周围几乎没有其他魔法,例如 if-let 语法,还有 nilOptional.None 的同义词(我相信它们实际上是相同的)。但Optional 确实只是一个泛型类型,实现为枚举。它只是编译器知道的特殊类型。

    然后是ImplicitlyUnwrappedOptional&lt;Wrapped&gt;。这也只是一个枚举(在 Swift 2.2 中;这将在 Swift 3 中改变)。

    public enum ImplicitlyUnwrappedOptional<Wrapped> : _Reflectable, NilLiteralConvertible {
        case None
        case Some(Wrapped)
        // ...
    }
    

    这与Optional 不同,也与Wrapped 不同。这是一个完全不同的类型。但它也有一些与之相关的魔法。首先,Wrapped! 类型是ImplicitlyUnwrappedOptional&lt;Wrapped&gt; 的语法糖。再说一次,这只是糖。两者是相同的(在 Swift 2.2 中,而不是在 Swift 3 中)。接下来,如果IUO&lt;Wrapped&gt;出现在期望Wrapped的地方,则会自动转换为Wrapped,如果没有值则crash。如果在需要Wrapped? 的地方找到它,它将自动转换为Wrapped?。这些很神奇,这就是为什么有时StringString! 似乎 是同一类型。这只是编译器通过添加一个不可见的转换步骤神奇地为您“使其工作”。这并不意味着它们真的是同一类型。

    IUO 在桥接某些 Objective-C 模式时非常有用,尤其是涉及 Storyboard 时,应避免在这些情况之外使用。即使在这些情况下,IUO 也只是为了方便。你可以使用常规的 Optionals 来做所有相同的事情,你只需要更频繁地使用if-let 检查它们。使用 Optionals 比使用 IUO 安全得多。很容易想到“我肯定知道这个值总是在使用之前就设置好了”。就在这周,我追赶了一个撞车者,因为这件事是错误的。 “它应该是”和“它必须是”之间是有区别的。但是,在 Storyboard 中使用 Optional 完全安全可能会非常不方便,并且可能会掩盖一些错误(通过什么都不做而不是崩溃),所以这是 IUO 最常见的地方。

    IUO 属性曾经对处理失败的init 方法很有价值。这在 Swift 2.2 中不再是问题,所以这种使用已经消失了。我遇到的最后一个“纯 Swift”使用是当你必须将 self 传递给你作为属性存储的东西的初始化程序时(此时你不能传递 self,因为你的所有属性都已初始化)。这是一个不幸的用例,非常混乱,我希望我们能找到解决办法。在这些情况之外,您应该避免隐式展开的 Optional。

    【讨论】:

      【解决方案2】:

      StringString! 既相同又不同。如果将 let 或 var 声明为 String,则必须在 init 方法中设置一些值。如果您将其声明为String!,您可以在需要时设置值,但您必须在读取此值之前进行设置。

      因此,如果您的 _name 将是 nil,您的应用将会崩溃。

      如果将其声明为String,编译器会在第一次读取之前保证_name != nil。如果你声明为String!,你应该保证它。

      由于这个原因,这些东西具有相同的类型:

      var test1: String = "test"
      var test2: String! = "test"
      
      if test1 is String { // True
          print("test1 is String")
      }
      if test2 is String { // True
          print("test2 is String")
      }
      

      这些类型是相同的,但是这东西是不同的

      【讨论】:

      • StringString! 本身并不相等,它们有一些细微的差别。后者是一个 ImplicitlyUnwrappedOptional 包装一个 String,即使它是“隐式展开”,您也可以使用例如条件绑定块中的String! 实例(if let foo = bar { } 用于bar 类型为String!),而您可能不是“纯”String 实例。然而,它们确实在实践中有许多相似之处,例如安全不是其中之一。
      • @Artyom,根据你的解释,是不是最好用String类型声明_name而不是String!,这样编译器会一直提醒我设置一个值在init 中,我的应用程序崩溃的机会将更少,否则如果像我现在所做的那样,_name 的类型为String!,我可能会得到一个 nil 值并意外崩溃我的应用程序?
      • @Leem.fin 你不能在没有名字的情况下初始化你的示例类,但是你可以在之后将 nil 设置为 name,如果你的变量被访问,它可能会导致崩溃
      • 最好将变量声明为String,但你不能在任何地方都这样做。例如,您的@IBOutlet 始终为ImplicitlyUnwrappedOptional(例如UIView!),因为它们的值无法在初始化时设置
      • is String 通过并不意味着String!String 是同一类型。这只是意味着编译器愿意在它们之间进行隐式转换。比较 test1.dynamicTypetest2.dynamicType 以查看差异。还值得注意的是,虽然您可以@IBOutlet 使用IUO,但您不必这样做。你也可以使用常规的 Optional。 IUO 是一种方便(并不总是主要的),而不是那里的要求。
      【解决方案3】:

      注意这两个函数的区别:

      func f() {
          let x: String? = nil
          guard let y: String = x else {
              print("failed")
              return
          }
      }
      
      func g() {
          let x: String? = nil
          guard let y: String! = x else {
              print("failed")
              return
          }
          print(y.dynamicType, y)
      }
      
      f() # failed
      g() # Optional<String> nil
      

      【讨论】:

      • 这是一个非常有趣的极端案例,但可能会让人困惑而不是启发(它甚至可能是一个编译器错误,但如果是,它在 Swift 3 中将无关紧要)。输出表明 String!String? 是同一类型,但事实并非如此。我确信guard-let 正在将x 提升为String??,这就是它成功的原因。提升的String?? 有值;该值为.Some(nil)(与nil 完全不同)。将您的示例与这个更简单的示例进行比较:let x: String? = nil; let z: String! = x; print(z.dynamicType).
      • @RobNapier 我确实认为这是一个编译器错误,仅限于在if letguard let 子句中明确声明绑定到ImplicitlyUnwrappedOptional&lt;T&gt; 的类型,正如我之前对in this thread 的思考。错误报告已提交here (SR-1123),但正如您所说,它应该在 Swift 3 中无关紧要(并且已经隐式修复:)。
      【解决方案4】:

      您最好阅读swift documentation,而不是这里的任何答案。正确理解 swift 的类型系统非常重要,所以要先搞清楚。

      【讨论】:

        【解决方案5】:

        String!String? 都是可选类型。

        使用String? 要求您测试该值是nil (.None) 还是值(.Some)。例如使用保护语句或 if-let 子句。

        String! 是一个可选的,假定为非零并自动解包。这使您可以编写看起来更清晰的代码,并避免在使用值的任何地方展开。但是:如果您的值为 nil,则会发生致命错误。

        【讨论】:

        • 这在 Swift 3 中是正确的,但在 2.2 中是不正确的。 String!ImplicitlyUnwrappedOptional&lt;String&gt;,与 Optional&lt;String&gt; 不同。
        • ImplicitlyUnwrappedOptional 的子类型 Optional 吗?
        • 没有。不能有枚举的子类型。
        • 我改了答案。
        猜你喜欢
        • 2020-04-21
        • 2013-05-05
        • 2015-12-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多