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

Posted

技术标签:

【中文标题】Swift 字符串与字符串!与字符串?【英文标题】:Swift string vs. string! vs. string? 【发布时间】:2016-05-27 22:39:40 【问题描述】:

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

有些无关

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

@IBOutlet 属性具有非可选类型“UILabel”

然后 Xcode 为您提供 2 个选项来修复它,您可以这样做:

修复它添加?形成可选类型 UIlabel? 修复它添加!来形成 隐式展开的可选类型 UIlabel?

但是对于字符串,您可以只输入 string 而不输入 ?!,您不会收到错误为什么会这样?

如果name 未设置会怎样?那么我们会有一个 nil 没有使用 ?!Swift 来满足“类型安全”?

示例:

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

我最大的困惑是我们什么时候希望使用 Optional?

【问题讨论】:

【参考方案1】:

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

简而言之:

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

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

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

【讨论】:

你能详细说明你的最后一段吗?我的主要困惑实际上是string vs string!,我认为所有对象都是可选的......我的意思是没有“字符串类型”这样的东西,只有“可选字符串”作为一种类型 我强烈建议您仔细阅读我发布的链接。它很好地解释了这一切。 Optional 只是一个枚举。它的值为NoneSome。在后一种情况下,它具有与之相关的潜在价值。当您说let s : String! = "a string" 之类的内容时,您只是在使用let s : Optional<String> = Optional("a string") 的缩写形式,其附加结果是所有对 var 的访问都试图强制解开其中的值。 我已经阅读了这些段落和您的帖子 无数 次,但我仍然对 StringString! 之间的区别感到有些困惑,它们都会在运行时崩溃— 如果值为nil。对?这就是让他们与众不同的原因吗?如果我们使用String! 作为名称或详细信息,是否有意义? @Honey they would both crash during runtime—if the value is nil. right? 来自我的回答:String represents a String that's guaranteed to be present. There is no way this value can be nil, thus it's safe to use directly. @Honey 是的,这就是 Optionals 的重点:消除/减少 nil 值的运行时不确定性,并尽可能将错误处理移至编译时检查。这就是为什么不鼓励隐式展开 (String!) 的原因,因为它基本上完全放弃了这些功能。【参考方案2】:

对于您的 PancakeHouse 结构,name 是非可选的。这意味着它的name 属性不能为零。每当初始化PancakeHouse 的实例时,编译器将强制要求将name 初始化为非零值。它通过要求在为PancakeHouse 定义的任何和所有初始化程序中设置name 来做到这一点。

对于@IBOutlet 属性,这是不可能的。当一个 Interface Builder 文件(XIB 或 Storyboard)被取消归档/加载时,IB 文件中定义的出口被设置,但这总是发生在其中的对象被初始化之后(例如,在视图加载期间)。因此,在初始化之后但在设置出口之前必然有一段时间,在此期间,出口将为零。 (还有一个问题是 IB 文件中可能没有设置 outlet,编译器不会/无法检查。)这解释了为什么 @IBOutlet 属性 必须 是可选的。

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

【讨论】:

嗯...所以它是“强制初始化”,这是造成差异!谢谢! 这是否意味着隐式展开仅用于 IBOutlets?关于'string'和'string!'的区别'string' 永远不会因为初始化程序而使你的应用程序崩溃......但是'string!'如果未设置可能会使应用程序崩溃?对吗? 不,隐式展开可用于任何可选。如果值实际上是 nil,则隐式展开的选项(如 string!)将崩溃。您通常只在您确定某个值不会为 nil 的情况下才使用 !,这种情况很少见。有些人会争辩说好的 Swift 代码从不使用!。这一切都在Swift book 中进行了介绍(请参阅“隐式解包选项”部分)。 我读这本书已经有一段时间了。我相信我现在对tiny有了更好的理解,我想我的问题的根源是:我们什么时候应该使用可选的,什么时候不应该或者基本上什么时候应该让我们的程序崩溃通过不设置隐式展开属性?我们应该在什么时候希望我们的程序给出 not setting 存储属性的错误? 在学习了所有答案之后,我得写一个答案。您能否评论/更正我自己的答案,看看它是否比您的答案更好地解决了这个问题(没有冒犯),因为我的主要困惑点是字符串与字符串!【参考方案3】:

一开始,整个“可选”的事情让我很不爽。让我“点击”的原因是当我不再将它们视为“字符串”对象并开始将它们视为泛型时。就像具有“String”通用标识符的“Array”是一个数组,如果它有值,则包含字符串......“String?”是一个可选的,如果它有一个值,就是一个字符串。

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

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

字符串! - 这是一个可选的,带有语法糖,可让您直接访问其值,就好像它只是一个字符串一样。它可能是 nil,但无论出于何种原因,变量周围的上下文对你眨了眨眼,说“别担心,它会有一个值。”

【讨论】:

【参考方案4】:

感谢 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(),因为我告诉我的代码这是一个可选的意思,它将在实例化/编译之后从其他地方设置。这意味着如果您没有设置 lldb,它可以通过我运行时错误。 (错误会发生在线CCCC)

【讨论】:

这样做并不酷。最好将这些评论回复到其他人的答案中,以正确归因于他们的工作 @AMomchilov 我没有这样做的习惯。只是“据我所知”所有答案的组合并不能解释StringString!。我也确实要求他在接受我自己的答案之前先看看我的答案。并不是我获得了积分,事实上我失去了2分。我将他们的工作归于第一行。您认为我的回答有任何技术问题吗? 这只是关于 SO 的约定问题。将答案发布为“猜测”然后要求其他人确认,无论是针对您自己的问题还是其他问题,通常都不太受欢迎。这类事情就是 cmets 的用途。您可以要求其他 OP 澄清他们的答案,甚至可以自己提出修改建议。 @AMomchilov 感谢您的额外解释。我明白你在说什么。但我无法将所有这些都纳入评论。 :) 此外,在我看来,我的回答非常清楚地区分了StringString!。如果您发现不清楚,您可以随时要求澄清,我很乐意为您提供帮助。

以上是关于Swift 字符串与字符串!与字符串?的主要内容,如果未能解决你的问题,请参考以下文章

Swift3.0语言教程获取字符串编码与哈希地址

swift中的文字字符串与`String`

swift 将字符串与颜色字体和其他字符串归

如何检查我的字符串是不是与正则表达式匹配 - Swift 3 [重复]

Swift3.0P1 语法指南——字符串与字符

Swift3.0-字符串和字符