In Swift, Optional<Wrapped> is an option type: it can contain any value from the original ("Wrapped") type, or no value at all (the special value nil). An optional value must beunwrappedbefore it can be used.
Optional is a generic type, which means that Optional<Int> and Optional<String> are distinct types — the type inside <> is called the Wrapped type. Under the hood, an Optional is an enum with two cases: .some(Wrapped) and .none, where .none is equivalent to nil.
Optionals can be declared using the named type Optional<T>, or (most commonly) as a shorthand with a ? suffix.
var anInt: Int = 42
var anOptionalInt: Int? = 42
var anotherOptionalInt: Int? // `nil` is the default when no value is provided
var aVerboseOptionalInt: Optional<Int> // equivalent to `Int?`
anOptionalInt = nil // now this variable contains nil instead of an integer
Optionals are a simple yet powerful tool to express your assumptions while writing code. The compiler can use this information to prevent you from making mistakes. From The Swift Programming Language:
Swift is a type-safe language, which means the language helps you to be clear about the types of values your code can work with. If part of your code requires a String, type safety prevents you from passing it an Int by mistake.**Likewise, type safety prevents you from accidentally passing an optional String to a piece of code that requires a non-optional String.**Type safety helps you catch and fix errors as early as possible in the development process.
Some other programming languages also have generic option types: for example, Maybe in Haskell, option in Rust, and optional in C++17.
In programming languages without option types, a particular "sentinel" value is often used to indicate the absence of a valid value. In Objective-C, for example, nil (the null pointer) represents the lack of an object. For primitive types such as int, a null pointer can't be used, so you would need either a separate variable (such as value: Int and isValid: Bool) or a designated sentinel value (such as -1 or INT_MIN). These approaches are error-prone because it's easy to forget to check isValid or to check for the sentinel value. Also, if a particular value is chosen as the sentinel, that means it can no longer be treated as a valid value.
Option types such as Swift's Optional solve these problems by introducing a special, separate nil value (so you don't have to designate a sentinel value), and by leveraging the strong type system so the compiler can help you remember to check for nil when necessary.
Why did I get “Fatal error: Unexpectedly found nil while unwrapping an Optional value”?
In order to access an optional’s value (if it has one at all), you need tounwrapit. An optional value can be unwrapped safely or forcibly. If you force-unwrap an optional, and it didn't have a value, your program will crash with the above message.
Xcode will show you the crash by highlighting a line of code. The problem occurs on this line.
This crash can occur with two different kinds of force-unwrap:
1. Explicit Force Unwrapping
This is done with the ! operator on an optional. For example:
let anOptionalString: String?
print(anOptionalString!) // <- CRASH
Fatal error: Unexpectedly found nil while unwrapping an Optional value
As anOptionalString is nil here, you will get a crash on the line where you force unwrap it.
2. Implicitly Unwrapped Optionals
These are defined with a !, rather than a ? after the type.
var optionalDouble: Double! // this value is implicitly unwrapped wherever it's used
These optionals are assumed to contain a value. Therefore whenever you access an implicitly unwrapped optional, it will automatically be force unwrapped for you. If it doesn’t contain a value, it will crash.
print(optionalDouble) // <- CRASH
Fatal error: Unexpectedly found nil whileimplicitlyunwrapping an Optional value
In order to work out which variable caused the crash, you can hold ⌥ while clicking to show the definition, where you might find the optional type.
IBOutlets, in particular, are usually implicitly unwrapped optionals. This is because your xib or storyboard will link up the outlets at runtime, after initialization. You should therefore ensure that you’re not accessing outlets before they're loaded in. You also should check that the connections are correct in your storyboard/xib file, otherwise the values will be nil at runtime, and therefore crash when they are implicitly unwrapped. When fixing connections, try deleting the lines of code that define your outlets, then reconnect them.
When should I ever force unwrap an Optional?
Explicit Force Unwrapping
As a general rule, you should never explicitly force unwrap an optional with the ! operator. There may be cases where using ! is acceptable – but you should only ever be using it if you are 100% sure that the optional contains a value.
While there may be an occasion where you can use force unwrapping, as you know for a fact that an optional contains a value – there is not a single place where you cannot safely unwrap that optional instead.
Implicitly Unwrapped Optionals
These variables are designed so that you can defer their assignment until later in your code. It is your responsibility to ensure they have a value before you access them. However, because they involve force unwrapping, they are still inherently unsafe – as they assume your value is non-nil, even though assigning nil is valid.
You should only be using implicitly unwrapped optionals as a last resort. If you can use a lazy variable, or provide a default value for a variable – you should do so instead of using an implicitly unwrapped optional.
The simplest way to check whether an optional contains a value, is to compare it to nil.
if anOptionalInt != nil {
print("Contains a value!")
} else {
print("Doesn’t contain a value.")
}
However, 99.9% of the time when working with optionals, you’ll actually want to access the value it contains, if it contains one at all. To do this, you can use Optional Binding.
Optional Binding
Optional Binding allows you to check if an optional contains a value – and allows you to assign the unwrapped value to a new variable or constant. It uses the syntax if let x = anOptional {...} or if var x = anOptional {...}, depending if you need to modify the value of the new variable after binding it.
For example:
if let number = anOptionalInt {
print("Contains a value! It is \(number)!")
} else {
print("Doesn’t contain a number")
}
What this does is first check that the optional contains a value. If it does, then the ‘unwrapped’ value is assigned to a new variable (number) – which you can then freely use as if it were non-optional. If the optional doesn’t contain a value, then the else clause will be invoked, as you would expect.
What’s neat about optional binding, is you can unwrap multiple optionals at the same time. You can just separate the statements with a comma. The statement will succeed if all the optionals were unwrapped.
var anOptionalInt : Int?
var anOptionalString : String?
if let number = anOptionalInt, let text = anOptionalString {
print("anOptionalInt contains a value: \(number). And so does anOptionalString, it’s: \(text)")
} else {
print("One or more of the optionals don’t contain a value")
}
Another neat trick is that you can also use commas to check for a certain condition on the value, after unwrapping it.
if let number = anOptionalInt, number > 0 {
print("anOptionalInt contains a value: \(number), and it’s greater than zero!")
}
The only catch with using optional binding within an if statement, is that you can only access the unwrapped value from within the scope of the statement. If you need access to the value from outside of the scope of the statement, you can use a guard statement.
A guard statement allows you to define a condition for success – and the current scope will only continue executing if that condition is met. They are defined with the syntax guard condition else {...}.
So, to use them with an optional binding, you can do this:
guard let number = anOptionalInt else {
return
}
(Note that within the guard body, youmustuse one of the control transfer statements in order to exit the scope of the currently executing code).
If anOptionalInt contains a value, it will be unwrapped and assigned to the new number constant. The code after the guard will then continue executing. If it doesn’t contain a value – the guard will execute the code within the brackets, which will lead to transfer of control, so that the code immediately after will not be executed.
The real neat thing about guard statements is the unwrapped value is now available to use in code that follows the statement (as we know that future code can only execute if the optional has a value). This is a great for eliminating ‘pyramids of doom’ created by nesting multiple if statements.
For example:
guard let number = anOptionalInt else {
return
}
print("anOptionalInt contains a value, and it’s: \(number)!")
Guards also support the same neat tricks that the if statement supported, such as unwrapping multiple optionals at the same time and using the where clause.
Whether you use an if or guard statement completely depends on whether any future code requires the optional to contain a value.
Nil Coalescing Operator
The Nil Coalescing Operator is a nifty shorthand version of the ternary conditional operator, primarily designed to convert optionals to non-optionals. It has the syntax a ?? b, where a is an optional type and b is the same type as a (although usually non-optional).
It essentially lets you say “If a contains a value, unwrap it. If it doesn’t then return b instead”. For example, you could use it like this:
let number = anOptionalInt ?? 0
This will define a number constant of Int type, that will either contain the value of anOptionalInt, if it contains a value, or 0 otherwise.
It’s just shorthand for:
let number = anOptionalInt != nil ? anOptionalInt! : 0
Optional Chaining
You can use Optional Chaining in order to call a method or access a property on an optional. This is simply done by suffixing the variable name with a ? when using it.
For example, say we have a variable foo, of type an optional Foo instance.
var foo : Foo?
If we wanted to call a method on foo that doesn’t return anything, we can simply do:
foo?.doSomethingInteresting()
If foo contains a value, this method will be called on it. If it doesn’t, nothing bad will happen – the code will simply continue executing.
(This is similar behaviour to sending messages to nil in Objective-C)
This can therefore also be used to set properties as well as call methods. For example:
foo?.bar = Bar()
Again, nothing bad will happen here if foo is nil. Your code will simply continue executing.
Another neat trick that optional chaining lets you do is check whether setting a property or calling a method was successful. You can do this by comparing the return value to nil.
(This is because an optional value will return Void? rather than Void on a method that doesn’t return anything)
For example:
if (foo?.bar = Bar()) != nil {
print("bar was set successfully")
} else {
print("bar wasn’t set successfully")
}
However, things become a little bit more tricky when trying to access properties or call methods that return a value. Because foo is optional, anything returned from it will also be optional. To deal with this, you can either unwrap the optionals that get returned using one of the above methods – or unwrap foo itself before accessing methods or calling methods that return values.
Also, as the name suggests, you can ‘chain’ these statements together. This means that if foo has an optional property baz, which has a property qux – you could write the following:
let optionalQux = foo?.baz?.qux
Again, because foo and baz are optional, the value returned from qux will always be an optional regardless of whether qux itself is optional.
map and flatMap
An often underused feature with optionals is the ability to use the map and flatMap functions. These allow you to apply non-optional transforms to optional variables. If an optional has a value, you can apply a given transformation to it. If it doesn’t have a value, it will remain nil.
For example, let’s say you have an optional string:
let anOptionalString:String?
By applying the map function to it – we can use the stringByAppendingString function in order to concatenate it to another string.
Because stringByAppendingString takes a non-optional string argument, we cannot input our optional string directly. However, by using map, we can use allow stringByAppendingString to be used if anOptionalString has a value.
For example:
var anOptionalString:String? = "bar"
anOptionalString = anOptionalString.map {unwrappedString in
return "foo".stringByAppendingString(unwrappedString)
}
print(anOptionalString) // Optional("foobar")
However, if anOptionalString doesn’t have a value, map will return nil. For example:
var anOptionalString:String?
anOptionalString = anOptionalString.map {unwrappedString in
return "foo".stringByAppendingString(unwrappedString)
}
print(anOptionalString) // nil
flatMap works similarly to map, except it allows you to return another optional from within the closure body. This means you can input an optional into a process that requires a non-optional input, but can output an optional itself.
try!
Swift's error handling system can be safely used with Do-Try-Catch:
do {
let result = try someThrowingFunc()
} catch {
print(error)
}
If someThrowingFunc() throws an error, the error will be safely caught in the catch block.
The error constant you see in the catch block has not been declared by us - it's automatically generated by catch.
You can also declare error yourself, it has the advantage of being able to cast it to a useful format, for example:
do {
let result = try someThrowingFunc()
} catch let error as NSError {
print(error.debugDescription)
}
Using try this way is the proper way to try, catch and handle errors coming from throwing functions.
There's also try? which absorbs the error:
if let result = try? someThrowingFunc() {
// cool
} else {
// handle the failure, but there's no error information available
}
But Swift's error handling system also provides a way to "force try" with try!:
let result = try! someThrowingFunc()
The concepts explained in this post also apply here: if an error is thrown, the application will crash.
You should only ever use try! if you can prove that its result will never fail in your context - and this is very rare.*
Most of the time you will use the complete Do-Try-Catch system - and the optional one, try?, in the rare cases where handling the error is not important.
当我尝试通过Prepare For Segue方法设置我的插座值时,有一次我遇到了这个错误,如下所示:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? DestinationVC{
if let item = sender as? DataItem{
// This line pops up the error
destination.nameLabel.text = item.name
}
}
}
然后我发现我无法设置目的地控制器插座的值,因为控制器尚未加载或初始化。
所以我这样解决了这个问题:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? DestinationVC{
if let item = sender as? DataItem{
// Created this method in the destination Controller to update its outlets after it's being initialized and loaded
destination.updateView(itemData: item)
}
}
}
目标控制器:
// This variable to hold the data received to update the Label text after the VIEW DID LOAD
var name = ""
// Outlets
@IBOutlet weak var nameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
nameLabel.text = name
}
func updateView(itemDate: ObjectModel) {
name = itemDate.name
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var c1 = NormalContact()
let c3 = BadContact()
c1.address.city = c3.address.city // compiler hides the truth from you and then you sudden get a crash
}
}
struct NormalContact {
var address : Address = Address(city: "defaultCity")
}
struct BadContact {
var address : Address!
}
struct Address {
var city : String
}
c1.address.city = c2.address!.city // ERROR: Fatal error: Unexpectedly found nil while unwrapping an Optional value
你现在能告诉我nil是哪个物体吗?
这一次,代码对您来说更清楚了。您可以合理地认为很可能是address参数被强制解包。
完整的代码为:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var c1 = NormalContact()
let c2 = GoodContact()
c1.address.city = c2.address!.city
c1.address.city = c2.address?.city // not compile-able. No deceiving by the compiler
c1.address.city = c2.address.city // not compile-able. No deceiving by the compiler
if let city = c2.address?.city { // safest approach. But that's not what I'm talking about here.
c1.address.city = city
}
}
}
struct NormalContact {
var address : Address = Address(city: "defaultCity")
}
struct GoodContact {
var address : Address?
}
struct Address {
var city : String
}
class ResultViewController: UIViewController {
@IBOutlet weak var resultLabel: UILabel!
var bmiValue=""
override func viewDidLoad() {
super.viewDidLoad()
print(bmiValue)
resultLabel.text=bmiValue //where bmiValue was nil , I fixed it and problem was solved
}
@IBAction func recaculateBmi(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
}
15条答案
按热度按时间ymdaylpp1#
Background: What’s an Optional?
In Swift,
Optional<Wrapped>
is an option type: it can contain any value from the original ("Wrapped") type, or no value at all (the special valuenil
). An optional value must beunwrappedbefore it can be used.Optional is a generic type, which means that
Optional<Int>
andOptional<String>
are distinct types — the type inside<>
is called the Wrapped type. Under the hood, an Optional is an enum with two cases:.some(Wrapped)
and.none
, where.none
is equivalent tonil
.Optionals can be declared using the named type
Optional<T>
, or (most commonly) as a shorthand with a?
suffix.Optionals are a simple yet powerful tool to express your assumptions while writing code. The compiler can use this information to prevent you from making mistakes. From The Swift Programming Language:
Swift is a type-safe language, which means the language helps you to be clear about the types of values your code can work with. If part of your code requires a
String
, type safety prevents you from passing it anInt
by mistake.**Likewise, type safety prevents you from accidentally passing an optionalString
to a piece of code that requires a non-optionalString
.**Type safety helps you catch and fix errors as early as possible in the development process.Some other programming languages also have generic option types: for example, Maybe in Haskell, option in Rust, and optional in C++17.
In programming languages without option types, a particular "sentinel" value is often used to indicate the absence of a valid value. In Objective-C, for example,
nil
(the null pointer) represents the lack of an object. For primitive types such asint
, a null pointer can't be used, so you would need either a separate variable (such asvalue: Int
andisValid: Bool
) or a designated sentinel value (such as-1
orINT_MIN
). These approaches are error-prone because it's easy to forget to checkisValid
or to check for the sentinel value. Also, if a particular value is chosen as the sentinel, that means it can no longer be treated as a valid value.Option types such as Swift's
Optional
solve these problems by introducing a special, separatenil
value (so you don't have to designate a sentinel value), and by leveraging the strong type system so the compiler can help you remember to check for nil when necessary.Why did I get “Fatal error: Unexpectedly found nil while unwrapping an Optional value”?
In order to access an optional’s value (if it has one at all), you need tounwrapit. An optional value can be unwrapped safely or forcibly. If you force-unwrap an optional, and it didn't have a value, your program will crash with the above message.
Xcode will show you the crash by highlighting a line of code. The problem occurs on this line.
This crash can occur with two different kinds of force-unwrap:
1. Explicit Force Unwrapping
This is done with the
!
operator on an optional. For example:Fatal error: Unexpectedly found nil while unwrapping an Optional value
As
anOptionalString
isnil
here, you will get a crash on the line where you force unwrap it.2. Implicitly Unwrapped Optionals
These are defined with a
!
, rather than a?
after the type.These optionals are assumed to contain a value. Therefore whenever you access an implicitly unwrapped optional, it will automatically be force unwrapped for you. If it doesn’t contain a value, it will crash.
Fatal error: Unexpectedly found nil whileimplicitlyunwrapping an Optional value
In order to work out which variable caused the crash, you can hold ⌥ while clicking to show the definition, where you might find the optional type.
IBOutlets, in particular, are usually implicitly unwrapped optionals. This is because your xib or storyboard will link up the outlets at runtime, after initialization. You should therefore ensure that you’re not accessing outlets before they're loaded in. You also should check that the connections are correct in your storyboard/xib file, otherwise the values will be
nil
at runtime, and therefore crash when they are implicitly unwrapped. When fixing connections, try deleting the lines of code that define your outlets, then reconnect them.When should I ever force unwrap an Optional?
Explicit Force Unwrapping
As a general rule, you should never explicitly force unwrap an optional with the
!
operator. There may be cases where using!
is acceptable – but you should only ever be using it if you are 100% sure that the optional contains a value.While there may be an occasion where you can use force unwrapping, as you know for a fact that an optional contains a value – there is not a single place where you cannot safely unwrap that optional instead.
Implicitly Unwrapped Optionals
These variables are designed so that you can defer their assignment until later in your code. It is your responsibility to ensure they have a value before you access them. However, because they involve force unwrapping, they are still inherently unsafe – as they assume your value is non-nil, even though assigning nil is valid.
You should only be using implicitly unwrapped optionals as a last resort. If you can use a lazy variable, or provide a default value for a variable – you should do so instead of using an implicitly unwrapped optional.
However, there are a few scenarios where implicitly unwrapped optionals are beneficial, and you are still able to use various ways of safely unwrapping them as listed below – but you should always use them with due caution.
How can I safely deal with Optionals?
The simplest way to check whether an optional contains a value, is to compare it to
nil
.However, 99.9% of the time when working with optionals, you’ll actually want to access the value it contains, if it contains one at all. To do this, you can use Optional Binding.
Optional Binding
Optional Binding allows you to check if an optional contains a value – and allows you to assign the unwrapped value to a new variable or constant. It uses the syntax
if let x = anOptional {...}
orif var x = anOptional {...}
, depending if you need to modify the value of the new variable after binding it.For example:
What this does is first check that the optional contains a value. If it does, then the ‘unwrapped’ value is assigned to a new variable (
number
) – which you can then freely use as if it were non-optional. If the optional doesn’t contain a value, then the else clause will be invoked, as you would expect.What’s neat about optional binding, is you can unwrap multiple optionals at the same time. You can just separate the statements with a comma. The statement will succeed if all the optionals were unwrapped.
Another neat trick is that you can also use commas to check for a certain condition on the value, after unwrapping it.
The only catch with using optional binding within an if statement, is that you can only access the unwrapped value from within the scope of the statement. If you need access to the value from outside of the scope of the statement, you can use a guard statement.
A guard statement allows you to define a condition for success – and the current scope will only continue executing if that condition is met. They are defined with the syntax
guard condition else {...}
.So, to use them with an optional binding, you can do this:
(Note that within the guard body, youmustuse one of the control transfer statements in order to exit the scope of the currently executing code).
If
anOptionalInt
contains a value, it will be unwrapped and assigned to the newnumber
constant. The code after the guard will then continue executing. If it doesn’t contain a value – the guard will execute the code within the brackets, which will lead to transfer of control, so that the code immediately after will not be executed.The real neat thing about guard statements is the unwrapped value is now available to use in code that follows the statement (as we know that future code can only execute if the optional has a value). This is a great for eliminating ‘pyramids of doom’ created by nesting multiple if statements.
For example:
Guards also support the same neat tricks that the if statement supported, such as unwrapping multiple optionals at the same time and using the
where
clause.Whether you use an if or guard statement completely depends on whether any future code requires the optional to contain a value.
Nil Coalescing Operator
The Nil Coalescing Operator is a nifty shorthand version of the ternary conditional operator, primarily designed to convert optionals to non-optionals. It has the syntax
a ?? b
, wherea
is an optional type andb
is the same type asa
(although usually non-optional).It essentially lets you say “If
a
contains a value, unwrap it. If it doesn’t then returnb
instead”. For example, you could use it like this:This will define a
number
constant ofInt
type, that will either contain the value ofanOptionalInt
, if it contains a value, or0
otherwise.It’s just shorthand for:
Optional Chaining
You can use Optional Chaining in order to call a method or access a property on an optional. This is simply done by suffixing the variable name with a
?
when using it.For example, say we have a variable
foo
, of type an optionalFoo
instance.If we wanted to call a method on
foo
that doesn’t return anything, we can simply do:If
foo
contains a value, this method will be called on it. If it doesn’t, nothing bad will happen – the code will simply continue executing.(This is similar behaviour to sending messages to
nil
in Objective-C)This can therefore also be used to set properties as well as call methods. For example:
Again, nothing bad will happen here if
foo
isnil
. Your code will simply continue executing.Another neat trick that optional chaining lets you do is check whether setting a property or calling a method was successful. You can do this by comparing the return value to
nil
.(This is because an optional value will return
Void?
rather thanVoid
on a method that doesn’t return anything)For example:
However, things become a little bit more tricky when trying to access properties or call methods that return a value. Because
foo
is optional, anything returned from it will also be optional. To deal with this, you can either unwrap the optionals that get returned using one of the above methods – or unwrapfoo
itself before accessing methods or calling methods that return values.Also, as the name suggests, you can ‘chain’ these statements together. This means that if
foo
has an optional propertybaz
, which has a propertyqux
– you could write the following:Again, because
foo
andbaz
are optional, the value returned fromqux
will always be an optional regardless of whetherqux
itself is optional.map
andflatMap
An often underused feature with optionals is the ability to use the
map
andflatMap
functions. These allow you to apply non-optional transforms to optional variables. If an optional has a value, you can apply a given transformation to it. If it doesn’t have a value, it will remainnil
.For example, let’s say you have an optional string:
By applying the
map
function to it – we can use thestringByAppendingString
function in order to concatenate it to another string.Because
stringByAppendingString
takes a non-optional string argument, we cannot input our optional string directly. However, by usingmap
, we can use allowstringByAppendingString
to be used ifanOptionalString
has a value.For example:
However, if
anOptionalString
doesn’t have a value,map
will returnnil
. For example:flatMap
works similarly tomap
, except it allows you to return another optional from within the closure body. This means you can input an optional into a process that requires a non-optional input, but can output an optional itself.try!
Swift's error handling system can be safely used with Do-Try-Catch:
If
someThrowingFunc()
throws an error, the error will be safely caught in thecatch
block.The
error
constant you see in thecatch
block has not been declared by us - it's automatically generated bycatch
.You can also declare
error
yourself, it has the advantage of being able to cast it to a useful format, for example:Using
try
this way is the proper way to try, catch and handle errors coming from throwing functions.There's also
try?
which absorbs the error:But Swift's error handling system also provides a way to "force try" with
try!
:The concepts explained in this post also apply here: if an error is thrown, the application will crash.
try!
if you can prove that its result will never fail in your context - and this is very rare.*Most of the time you will use the complete Do-Try-Catch system - and the optional one,
try?
, in the rare cases where handling the error is not important.Resources
fsi0uk1n2#
TL;灾难恢复应答
对于very few exceptions,这条规则是金科玉律:
避免使用
!
声明变量可选(
?
),不隐式展开选项(IUO)(!
)换句话说,应该使用:
var nameOfDaughter: String?
不是:
var nameOfDaughter: String!
使用
if let
或guard let
解包可选变量可以像这样展开变量:
或者像这样:
这个答案非常简洁,for full comprehension read accepted answer
资源
4ktjp1zp3#
这个问题一直都会出现。这是新的SWIFT开发人员首先要解决的问题之一。
背景:
SWIFT使用“选项”的概念来处理可能包含值或不包含值的值。在其他语言(如C)中,您可以将值0存储在变量中,以指示该变量不包含任何值。但是,如果0是一个有效的值呢?然后您可以使用-1。如果-1是有效的值,该怎么办?诸若此类。
SWIFT可选选项允许您设置任何类型的变量以包含有效值或不包含任何值。
当您将变量声明为Mean(类型x或无值)时,在类型后面加一个问号。
可选实际上是一个容器,它要么包含给定类型的变量,要么不包含任何变量。
为了获取内部的值,需要对可选的进行“解包”。
“!”运算符是“强制展开”运算符。它说“相信我。我知道我在做什么。我保证当这段代码运行时,变量不会包含空。”如果你错了,你就会崩溃。
除非你真的确实知道你在做什么,否则避免“!”强制展开操作符。这可能是初学SWIFT程序员崩溃的最大原因。
如何处理Optionals:
还有很多其他更安全的方式来处理选项。以下是一些(不是详尽的列表)
您可以使用“可选绑定”或“If let”来表示“如果此可选项包含一个值,则将该值保存到一个新的非可选变量中。如果可选项不包含值,则跳过此IF语句体”。
以下是
foo
可选绑定的可选绑定示例:请注意,当您使用可选投标时定义的变量只存在于if语句体中(仅“在作用域中”)。
或者,您可以使用Guard语句,它允许您在变量为nil时退出函数:
SWIFT 2中添加了Guard语句。Guard允许您保留代码中的“黄金路径”,并避免因使用“if let”可选绑定而导致的嵌套if级别不断增加。
还有一种被称为“零合并运算符”的结构。它的形式为“Optionalvar??Replace_val”。它返回一个非可选变量,其类型与可选中包含的数据类型相同。如果可选的包含nil,则返回“??”后的表达式的值。象征。
因此,您可以使用如下代码:
您也可以使用Try/Catch或Guard错误处理,但通常上面的其他技术之一更简洁。
编辑:
另一个稍微微妙一些的选项是“隐式展开的选项。当我们声明foo时,我们可以说:
在这种情况下,foo仍然是可选的,但您不必解开它来引用它。这意味着,当您尝试引用foo时,如果它为零,则会崩溃。
所以这段代码是:
将在引用Foo的CapitalizedString属性时崩溃,即使我们没有强制解包Foo。指纹看起来很好,但事实并非如此。
因此,您需要非常小心地处理隐式展开的选项。(甚至可能完全避开它们,直到你对选项有了扎实的理解。)
一句话:当你第一次学习SWIFT时,假装“!”性格不是语言的一部分。这很可能会给你带来麻烦。
vd8tlhqk4#
因为上面的答案清楚地解释了如何安全地使用可选选项。我会试着解释一下SWIFT中真正的选项是什么。
声明可选变量的另一种方法是
var i : Optional<Int>
而可选类型只是一个有两种情况的枚举,即
因此,将零赋给我们的变量‘i’。我们可以执行
var i = Optional<Int>.none
或赋值,我们将传递一些值var i = Optional<Int>.some(28)
根据斯威夫特的说法,nil指的是没有价值。要创建使用
nil
初始化的示例,我们必须符合名为ExpressibleByNilLiteral
的协议,如果您猜到了,这很好,只有Optionals
符合ExpressibleByNilLiteral
,不鼓励符合其他类型。ExpressibleByNilLiteral
有一个名为init(nilLiteral:)
的方法,该方法使用nil初始化示例。您通常不会调用此方法,根据SWIFT文档,不鼓励直接调用此初始化器,因为每当您使用nil
文本初始化可选类型时,编译器都会调用它。就连我自己也不得不(没有双关语的意思)思考选项:DHappy Swating All。
jdg4fx2g5#
首先,您应该知道什么是可选值。您可以转到The Swift Programming Language查看详细信息。
其次,您应该知道可选值有两种状态。一个是满值,另一个是空值。因此,在实现可选值之前,您应该检查它是哪种状态。
您可以使用
if let ...
或guard let ... else
等等。另一种方法是,如果您不想在实现之前检查变量状态,也可以使用
var buildingName = buildingName ?? "buildingName"
。mec1mxoz6#
当我尝试通过Prepare For Segue方法设置我的插座值时,有一次我遇到了这个错误,如下所示:
然后我发现我无法设置目的地控制器插座的值,因为控制器尚未加载或初始化。
所以我这样解决了这个问题:
目标控制器:
我希望这个答案能帮助任何有同样问题的人,因为我发现标记的答案是理解选项及其工作原理的很好的资源,但并没有直接解决问题本身。
kb5ga3dv7#
基本上,你试图在Swift只允许非零值的地方使用零值,告诉编译器相信那里永远不会有零值,从而允许你的应用程序编译。
有几种情况会导致此类致命错误:
1.强制展开:
如果
someVariable
为零,则会发生崩溃。通过执行强制展开,您将Nil检查责任从编译器转移到了您身上,基本上是通过执行强制展开,您向编译器保证了那里永远不会有空值。猜猜如果以某种方式零值以someVariable
结尾会发生什么?解决方案?使用可选绑定(也称为if-let),在那里进行变量处理:
1.强制(向下)投射:
在这里,通过强制强制转换,您可以告诉编译器不要再担心,因为您将始终在那里拥有一个
Rectangle
示例。只要这一点成立,你就不必担心。当您或项目中的同事开始传递非矩形值时,问题就开始了。解决方案?使用可选绑定(也称为if-let),在那里进行变量处理:
1.隐式展开的选项。让我们假设您有以下类定义:
现在,如果没有人通过将
name
设置为nil
来破坏name
属性,则它会按预期工作,但是如果User
是从缺少name
键的JSON初始化的,则在尝试使用该属性时会出现致命错误。解决方案?不要使用它们:),除非您102%确定该属性在需要使用时将始终具有非零值。在大多数情况下,转换为可选或非可选将起作用。将其设置为非可选也会导致编译器通过告诉您没有为该属性赋值的代码路径来帮助您
1.未连接或尚未连接的插座。这是场景#3的一个特殊情况。基本上,您有一些想要使用的加载了XIB的类。
现在,如果你错过了从XIB编辑器连接插座,那么只要你想使用插座,应用程序就会崩溃。解决方案?确保所有插座都已连接。或者对它们使用
?
运算符:emailTextField?.text = "my@email.com"
。或者将Outlet声明为可选,尽管在这种情况下,编译器将强制您在代码中将其全部解包。1.来自Objective-C的值,并且没有可为空性注解。让我们假设我们有以下的Objective-C类:
现在,如果未指定可为空性注解(显式或通过
NS_ASSUME_NONNULL_BEGIN
/NS_ASSUME_NONNULL_END
),则name
属性将作为String!
(IUO隐式展开可选)导入SWIFT。一旦某些快速代码想要使用该值,如果name
为空,它就会崩溃。解决方案?向您的Objective-C代码添加可为空性注解。不过要注意,当涉及到可为空性时,Objective-C编译器是有点宽松的,即使您显式地将它们标记为
nonnull
,您也可能最终得到空值。mrzz3bfm8#
这是一个更重要的评论,这就是为什么隐式展开的选项在调试
nil
值时可能具有欺骗性。请考虑以下代码:它编译时没有错误/警告:
但在运行时,它会给出以下错误:致命错误:在展开可选值时意外发现nil
能告诉我哪个对象是
nil
吗?你不能这么做!
完整的代码为:
长话短说,通过使用
var address : Address!
,您正在向其他读者隐藏变量可以是nil
的可能性。当它崩溃的时候,你就会说“见鬼?!我的address
不是可选的,为什么我要崩溃?!”因此,最好是这样写:
你现在能告诉我
nil
是哪个物体吗?这一次,代码对您来说更清楚了。您可以合理地认为很可能是
address
参数被强制解包。完整的代码为:
xyhw6mcr9#
当您声明了
@IBOutlet
,但没有连接到故事板时,错误EXC_BAD_INSTRUCTION
和fatal error: unexpectedly found nil while implicitly unwrapping an Optional value
出现得最多。你还应该学习其他答案中提到的选项是如何工作的,但这是唯一一次在我看来最常见的。
dm7nw8vv10#
如果在CollectionView中遇到此错误,请尝试创建CustomCell文件和Custom XIB。
将此代码添加到mainVC的ViewDidLoad()中。
xiozqbni11#
Xcode 12 iOS 14 SWIFT 5
我的问题是导航类型,因为我直接调用VIE控制器,而没有示例化故事板,所以还没有从故事板设置平均值数据。
导航时,请使用导航
希望它能奏效:-)
5ktev3wc12#
在进行从表视图控制器到视图控制器的分段时,我遇到了这个错误,因为我忘了在主情节提要中指定视图控制器的自定义类名。
一些简单的东西,如果其他一切看起来都好的话,就值得检查一下
3df52oht13#
如果在我的例子中,我将一个变量设置为UILabel,该变量为零。
所以我修复了它,此后它没有抛出错误。
代码片段
cnh2zyt314#
简单地说,您正在尝试使用可选变量的值,该值为零。快速修复可以使用
guard
或if let
,而不是像将!
放在变量末尾那样强制展开lsmepo6l15#
这是因为你试图使用一个可能为零的值,但你决定你不想检查它,而是在使用它时假设它的集,并将其定义为!,关于使用变量集作为强制展开有不同的哲学,有些人反对使用它,我个人认为它们对于总是会崩溃的事情是可以的,并且很容易推理,通常是引用资源,比如指向xib文件的出口,或者使用你的应用程序的图像,这些都是你资产的一部分,如果这些设置不正确,你的应用程序将立即崩溃。由于一个非常明显的原因,当创建对象的顺序可能不确定时,您可能会陷入困境,尝试推理解决方案可能会很困难,这通常意味着糟糕的设计,因为即使您将它们设置为可选的,对您的可选变量的调用可能永远也不会执行,一些项目可能出于安全原因要求使用强制展开,比如银行应用程序,因为它们希望应用程序崩溃,而不是以计划外的方式继续工作。