Swift 字符串对字符串!与字符串?

Swift string vs. string! vs. string?

我已阅读 this 问题和其他一些问题。但它们与我的问题

有些无关

对于UILabel如果你不指定?!你会得到这样的错误:

@IBOutlet property has non-optional type 'UILabel'

然后 Xcode 给你 2 个选项来修复它,你可以这样做:

fix-it Add ? to form the optional type UIlabel?
fix-it Add ! to form the implicitly unwrapped optional type UIlabel?

然而,对于字符串,您可以只键入 string 而无需 ?!,并且不会出现错误 这是为什么?

如果未设置 name 会怎样?那么我们会有一个 nil 不是使用 ?!Swift 来满足 'type-safety'?

示例:

struct PancakeHouse {
  let name: String // this doesn't have '?' nor '!'
  let photo: UIImage?
  let location: CLLocationCoordinate2D?
  let details: String
}

我的主要困惑是我们什么时候想要使用可选的?

所有这些都包含在文档中:The Swift Programming Language - The Basics

简而言之:

String 表示保证存在的 String。这个值不可能为nil,所以直接使用是安全的。

String?表示一个Optional<String>,可以是nil。您不能直接使用它,您必须先将其解包(使用 guardif let 或强制解包运算符 !)以生成 String。只要您不使用 ! 强制解包,这也是安全的。

String!也代表一个Optional<String>,可以是nil。但是,这个可选值可以用在需要非可选值的地方,这会导致隐式 forced 解包。这就像有一个 String? 并且总是用 ! 隐式地强制展开它。这些是危险的,因为 nil 的出现会使您的程序崩溃(除非您手动检查 nil)。

对于您的 PancakeHouse 结构,name 是非可选的。这意味着它的 name 属性 不能 为零。编译器将强制要求 name 在初始化 PancakeHouse 的实例时被初始化为非零值。它通过要求在为 PancakeHouse.

定义的任何和所有初始值设定项中设置 name 来做到这一点

对于 @IBOutlet 属性,这是不可能的。当 Interface Builder 文件(XIB 或 Storyboard)为 unarchived/loaded 时,设置了 IB 文件中定义的出口,但这总是在 中的对象初始化后发生(例如在视图加载期间)。所以在初始化之后,outlets设置之前必然有一段时间,在这段时间里,outlets会是nil。 (还有一个问题是出口可能没有在 IB 文件中设置,并且编译器不检查t/can。)这解释了为什么 @IBOutlet 属性 必须 可选。

@IBOutlet 的隐式展开可选 (!) 和常规可选 (?) 之间的选择取决于您。这些论点本质上是使用 ! 可以让你将 属性 视为非可选的,因此永远不会为零。如果由于某种原因它们为 nil,那通常会被认为是程序员错误(例如,插座未连接,或者您在视图加载完成之前访问了它,等等),在这些情况下,在开发过程中因崩溃而失败将帮助您抓住错误更快。另一方面,将它们声明为常规可选值,需要使用它们的任何代码显式处理由于某种原因可能未设置它们的情况。 Apple 选择隐式解包作为默认值,但有 Swift 程序员出于他们自己的原因,对 @IBOutlet 属性使用常规可选值。

整个 "optional" 事情一开始把我搞得一团糟。对我来说 "click" 的原因是当我不再将它们视为 "String" 对象并开始将它们视为泛型时。就像 "Array" 的通用标识符 "String" 是一个数组,如果它有值,则包含字符串...... "String?" 是一个 Optional,如果它有值,它就是一个字符串.

String - 这总是保证是某种字符串,永远不会为零。当你声明一个变量时,它必须被赋予一个值。

字符串? - 这是一个可选的。它可以是 nil,如果它有值,它将是一个字符串。为了访问可选项的值,您必须将其解包。

字符串! - 这是一个可选的语法糖,让您可以直接访问它的值,就好像它只是一个字符串。它可能是零,但无论出于何种原因,变量周围的上下文已经向你眨眼并说 "don't worry, it will have a value."

感谢 Andrew Madsen 的回答和所有其他回答,我自己学到了一些东西:

struct pairOfOptionalAndNonOptionalAndImplicitUnwrapped{
    var word1 :String?
    var word2 :String
    var word3: String!

    init (a:String, b: String, c: String){
        self.word1 = a // Line A
        self.word2 = b // Line B
        self.word3 = c // Line C
    } //Line BBBB


    func equal() ->Bool{
        return word1 == word2 + word3
    }
}

let wordParts = pairOfOptionalAndNonOptionalAndImplicitUnwrapped (a:"partTwo", b: "part", c:"Two")
wordParts.equal() // Line CCCC

  • 如果我只注释掉A行,我不会报错,因为它是可选的,optionals可以设置为nil。
  • 如果我只注释掉 B 行,我会在 BBBB 行得到一个编译错误,说:Return from initializer without initializing all stored properties,我得到这个是因为我声明了属性 word2 为非可选。我已经告诉编译器我保证让你设置...
  • 如果我只注释掉 C 行,我不会得到错误,除非我实际上 运行 我的对象上的一个方法。您可能得到的错误是:Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_1386_INVOP, subcode=0x0) 当且仅当我 运行 wordParts.equal(),因为我告诉我的代码这是一个可选的意思,它将从其他地方设置 instantiation/compilation之后。这意味着 lldb 可以通过我 运行time error 如果你没有设置它。 (错误会发生在 CCCC 线上)