- “This is the first day of my participation in the Gwen Challenge in November. Check out the details: The last Gwen Challenge in 2021”
preface
A good memory is not as good as bad writing. After learning, it is better to summarize and output knowledge. Therefore, a series of summaries are made for Swift learning:
- Function inventory in Swift
This second article in the Swift learning summary explores the options available to Swift.
Remember that when developing with OC, for an object that is not initialized, the default value is nil and the object can be reassigned to nil in subsequent operations. So we often get program errors because the object is nil, like inserting nil into an array causing a flash back.
But Swift is a type-safe language, Swift’s type does not allow variable values to be nil, either reference types or value types. However, in real development, there are cases where a variable can be nil, so Swfit also provides a special type, optional type, to handle this. Let’s explore the options below.
The nature of optional types
Swift’s type safety means that when you define a variable, given a type, you cannot assign another type of value to that variable. Of course, this does not mean that variable types must be explicitly specified when defined in Swift. Swift also has type inference, which automatically determines a variable type based on a given value. The following code looks like this:
let a:Int
a = "123" = > Cannot assign value of type 'String' to type 'Int'
var b = 20 // Assign the value to 20 and infer the type to Int
b = "swift" = > Cannot assign value of type 'String' to type 'Int'
var c:Int = nil = > 'nil' cannot initialize specified type 'Int'
var str: String = nil = > 'nil' cannot initialize specified type 'String'
Copy the code
In the above example code, a explicitly specifies type Int, and B is also specified as Int by type inference. Cannot assign value of type ‘String’ to type ‘Int’
For c and STR, if you assign the initial value to nil, you get an error nil that you cannot initialize for the specified type. Var c:Int = 0 var c:Int = 0
But in actual development process, we often unable to determine if a variable has a value, such as in a camera App, when we get the current camera, not sure if the camera is used by other App, or what’s the problem with the camera hardware itself, so not sure whether can succeed, So at this point we might get a nil value. At this point, you need to have a type that accepts nil and accepts normal values. In Swift, optional types are used to implement this type.
The optional type of Swift is defined asType +?, the specific code is as follows:A variable of an optional type can be given an initial value of the corresponding type. If not, the default value isnil
.
When an optional type has a specific value, it is not the same as the corresponding type. Take Int as an example: a
Is an optional Int,b
Is a common Int, although the value is 20, but the type of a printed isOptional(20). Take a look in the LLDBa
andb
, the results are as follows:You can see,b
Is a pureThe Int value 20And thea
Is in the20
It is covered with a layer, which is similar to a box, as shown below:
The red part of the figure represents the stored value. If the alternative type stores a value, the box stores the specific value, which in this case is Int 2. If the alternative type is nil, the box is empty.
What about multiple options? Can the options be wrapped with a layer of options? The code is as follows:
let result:Int?? = 20
Copy the code
Through LLDB debugging, you can see the actual structure as follows:
Drawing can be expressed as:
Print out the LLDB to see that the optional type is made up of oneOptional
The type of package, thenOptional
What is it? Actually,Optional
Is an enumerated type, whose definition can be found as follows:So the code shown below is equivalent:Optional uses generics to specify the type it wants to wrap, and Optional compliesExpressibleByNilLiteral
Protocol that complies with the protocol whose enumeration, struct, or class is initialized with an allowed value of nil.
The Optional enumeration contains cases of None and some. If the value is nil, it is None. If it has a value, it is wrapped as some, which shows the power of the Swift enumeration.
2. Forcibly unpack packets
Since an optional type wraps the value of the corresponding type in a box, is it possible to assign the value of an optional type to a variable of the corresponding type? A simple test can be performed with the following results:The answer is clearly no, and the compiler will report an error at compile timeValue of optional type 'Int? ' must be unwrapped to a value of type 'Int'
, Int? Must be unpacked into an Int.
Optional types of forced unpacking in Swift use one! The code is as follows:
let a:Int? = 20
var b:Int = 20
b = a!
Copy the code
Line 3 of code b = a! , the optional type Int a is unpacked to Int and assigned to b. Of course, a is still an optional type, and its value is still 20.
Note the following points when forcibly decompacking:
- 1. Forced unpacking does not affect the value of the original optional variable, which is still optional
- 2. For optional types whose value is nil, forced unpacking will flash back. Therefore, before using forced unpacking, you need to make sure that the value in the optional type is not
nil
Along with forced unpacking, there is a type, implicit unpacking optional, code as type +! . Examples are as follows:
let result:Int! = 20
let realInt:Int = result
Copy the code
You can see that result can be assigned directly to a realInt of an Int because the implicit unpacking option implicitly unpacks the variable without being aware of it. To be clear, though, implicit unpacking is still optional, and should be used with caution if you are not sure that variables will always have values.
Optional binding
When using optional forced unpacking, for example to prevent a flash back with a value of nil, we might write code like this:
let a:Int? = 20
var b:Int
if a ! = nil {
b = a!
}
Copy the code
This does provide a bit more security than unpacking, but Swift provides a more elegant way to solve this problem by optionally binding:
let a:Int? = 20
var b:Int
if let value = a {
b = value
} else {
print("The value of A is nil")}Copy the code
As shown in the condition after if in the code, the syntax for optional binding is let value = optional variable. Optional binding is used in conditions where if the optional variable is nil, the condition is false, and if the optional variable is not nil, it is automatically unpacked and assigned to value, except that value is scoped only after the if condition, not after the else condition {}.
If there are multiple optional bindings, separate them with commas (,) instead of &&, as shown in the figure:
Void merge operator
Swift also provides the air freight operator?? , which is defined as the following code
public func ?? <T> (optional: T? .defaultValue: @autoclosure(a)throws -> T?). rethrows -> T?
public func ?? <T> (optional: T? .defaultValue: @autoclosure(a)throws -> T) rethrows -> T
Copy the code
The null merge operator is a binary operator. Given two variables a and B, the null merge operator is a?? B. Note the following when using:
- a?? B, return b if a is nil, return A itself otherwise
- A needs to be optional, otherwise the compiler will not report an error, but it is meaningless
- B may or may not be optional
- Whether a and B are both optional or not, they should be stored of the same type, e.g. Int? <=> Int or Int? <=> Int?
- If b is not optional and the value of A is not nil, then when a is returned, it is automatically unpacked, and in fact B determines whether the return value is unpacked or not
4.1 Examples for using the null merge operator
Here are some examples of null merge operators, assuming that a and B store Int values:
A is nil, b is Int 2
let a:Int? = nil
let b:Int = 2
let result = a ?? b // result is the value of b and is of type Int
Copy the code
A is nil, b is optional Int value 2
let a:Int? = nil
let b:Int? = 2
let result = a ?? b // result为b的值 Optional(2)
Copy the code
A is not nil, b is Int
let a:Int? = 3
let b:Int = 2
let result = a ?? b // result is the value of a and has been unpacked to 3
Copy the code
A is not nil, and b is an optional Int
let a:Int? = 3
let b:Int? = 2
let result = a ?? b // result is the value of a and is still Optional(3)
Copy the code
There are also cases where multiple null merge operators are joined, as follows:
let a:Int? = 2
let b:Int? = nil
let c:Int = 4
let result = a ?? b ?? c // the result value is 2, which is the unpacked value of a
Copy the code
As the example shows, when more than one?? When used as a concatenation, the final variable c is used to determine the value of result. B gets an optional Int, right? Value 2, because c is an int, so the unpacked int value 2.
4.2 The null merge operator is bound to the optional
?? It can also be used in conjunction with optional bindings, as shown in the following code:
- Similar to the
a ! = nil || b ! = nil
let a:Int? = 2
let b:Int? = nil
if let result = a ?? b { // As long as either a or B is not empty, the condition can be entered
let c = result
print(c)
}
Copy the code
- Similar to the
a ! = nil && b ! = nil
let a:Int? = 2
let b:Int? = nil
if let c = a, let d = b { // Conditional judgment is entered only if neither a nor b is nil
print(c)
print(d)
}
Copy the code
With the above two methods, multiple optional nil values can be determined more simply, and if the condition is true, it can be automatically unpacked, using the unpacked value directly.
The guard statement
Guard statements are similar to if statements in that they are conditional statements.
guardconditionselse {
// Execute the code
}
Copy the code
Unlike the if statement, guard statements execute code in {} when the condition is false. See the following example:
let a:Int? = 20
guard a ! = nil else {
print("The value of A is nil")
return
}
print("Has a value of a\(a!)")
Copy the code
And the guard statement code block must haveReturn or throw an exception
Otherwise, the compiler will report the following error:
When you first encounter a guard statement, you might think you already have an if statement. Why do you need guard? And what Guard does, which if does, may feel redundant. However, after a period of development and a comparison of the two, the semantics of Guard are somewhat more explicit and the code is more readable. For example, change the above code to the if statement as follows:
if a = = nil {
print("The value of A is nil")
return
}
print("Has a value of a\(a!)")
Copy the code
Comparing the two pieces of code, semantically we match our expected value a! If guard is used to determine a==nil, guard is used to determine a! = nil, return if non-conforming, so guard is more suitable for fault tolerance.
Six, summarized
- The nature of the Swift optional type is
Optional
Enumeration, which containsnone
andsome
In both cases, None means that the current variable value is nil, and some means that the current variable value is not nil - The Swift option cannot be directly assigned to the variable corresponding to the type of its packaging. It should be assigned after unpacking. However, it needs to be noted that the unpacking must be forced under the condition that the value is guaranteed, otherwise Crash will occur
- For optional types, nulling can be done using optional bindings, which is more elegant
Different from OC, Swift pays more attention to security, especially in the processing of nil. Swift is more rigorous. Although it may not adapt to it at first contact, it can save a lot of fault-tolerant processing of nil in the development process, which also shows the power and exquisite design of Swift. This is a summary of the optional types in Swift, and your comments are welcome.