我已经阅读了这个问题和其他一些问题。但它们与我的问题有些无关
对于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
}
我的主要困惑是我们什么时候不想使用可选?
所有这些都包含在文档中:The Swift Programming Language - The Basics。
总之:
String
代表保证存在的String
。此值不可能为 nil,因此直接使用是安全的。
String?
表示一个Optional<String>
,可以nil
。你不能直接使用它,你必须首先解开它(使用guard
、if let
或强制解包运算符!
)来产生String
。只要你不用!
强行拆开它,这也是安全的。
String!
也表示一个Optional<String>
,可以nil
。但是,此可选可以在需要非可选的情况下使用,这会导致隐式强制解包。这就像有一个String?
,并且总是用!
隐含地强制打开它。这些是危险的,因为发生nil
会使程序崩溃(除非您手动检查nil
)。
对于您的PancakeHouse
结构,name
是非可选的。这意味着它的name
属性不能为零。编译器将强制要求在初始化PancakeHouse
实例时name
初始化为非 nil 值。它通过要求在为PancakeHouse
定义的任何和所有初始值设定项中设置name
来实现此目的。
对于@IBOutlet
属性,这是不可能的。当接口生成器文件(XIB 或情节提要)未存档/加载时,将设置 IB 文件中定义的出口,但这总是发生在其中的对象初始化之后(例如,在视图加载期间)。因此,在初始化之后但在设置出口之前必然有一段时间,在此期间,出口将为零。(还有一个问题是,出口可能没有在 IB 文件中设置,编译器不会/无法检查这一点。这就解释了为什么@IBOutlet
属性必须是可选的。
在隐式解包的可选(!
)和常规可选(@IBOutlet
?
)之间进行选择取决于你。这些参数本质上是,使用!
可以让您将属性视为非可选属性,因此永远不会为 nil。如果由于某种原因它们为 nil,这通常被认为是程序员错误(例如,插座未连接,或者您在视图加载完成之前访问了它等),在这些情况下,在开发过程中崩溃失败将帮助您更快地捕获错误。另一方面,将它们声明为常规可选,需要任何使用它们的代码显式处理由于某种原因可能未设置它们的情况。Apple 选择隐式解包作为默认值,但有些 Swift 程序员出于自己的原因,对@IBOutlet
属性使用常规可选。
整个"可选"的事情一开始让我很糟糕。 是什么让它对我来说"点击",是当我不再将它们视为"字符串"对象并开始将它们视为泛型时。 就像带有"字符串"通用标识符的"数组"是一个数组,如果它有值,则包含字符串......"字符串?"是一个可选,如果它有一个值,则是一个字符串。
字符串 - 这始终保证是某种字符串,并且永远不会为零。 声明变量时,必须为其赋值。
字符串? - 这是可选的。 它可以为 nil,如果它有一个值,它将是一个字符串。 为了访问可选值,您必须解开包装。
字符串! - 这是一个可选的,带有语法糖,可让您直接访问其值,就好像它只是一个字符串一样。 它可能为零,但无论出于何种原因,围绕变量的上下文都对你眨了眨眼,并说"别担心,它会有价值的。
感谢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 行,我不会得到任何错误,因为它是可选的,并且可选可以设置为 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,lldb可以通过我出现运行时错误。(错误将发生在CCCC线上)