An optional value
The sentries value
-
C code
int ch; while((ch = getchar()) ! = EOF) {printf("Read character %c\n", ch); } printf("Reached end-of-file\n"); Copy the code
-
C + + code
auto vec = {1.2.3}; auto iterator = std::find(vec.begin(),vec.end(),someValue); if(iterator ! = vec.end()) { std::cout << "vec contains" << *iterator << std:endl; } Copy the code
-
Java code
int i = Integer.getInteger("123") Copy the code
-
Objective-C
[[NSString alloc] initWithContentsOfURL: url encodig: NSUTF8StringEncoding error: &error]; Copy the code
In all of the examples above, the functions return a “magic” number to indicate that no real value is returned. Such values are called sentinel values.
Solve the magic number problem by enumeration
-
Most languages support some form of enumerated type, which is a safer way to express all the values that a type might contain. Optional is also implemented through enumeration
enum Optional<Wrapped> { case none case some(wrapped) } Copy the code
An overview of alternative values
- Many of the optional values in Swift come from the language’s built-in support
if let
- The **if let ** statement checks if the optional value is nil, and unpacks the optional value if it is not.
while let
- The while let statement is very similar to an if let in that it terminates the loop when a condition returns nil.
Double optional value
-
The wrapper type of an optional value is also an optional value case, which results in nesting of optional values.
-
Case-based pattern matching allows us to apply the same rules to if, for, and while that we use for switch matching. The most useful scenario is the combination of optional values.
if var and while var
-
In addition to let, you can use var to match if, while, and for. This allows you to change variables within a statement block:
let number = "1" if var i = Int(number) { i + = 1 print(i) } / / 2 Copy the code
Scope of optional values after unpacking
-
guard let
-
Never is also known as an uninhabited type. This type has no valid value and therefore cannot be built. In a generic environment, it is useful to combine Never and Result. For example, for A generic API that accepts Result
(both A and E are generic arguments), you can pass in A Result<… , Never> means that the result will Never contain the failure case, because such a case cannot be built.
,> -
In Swift, unattended types are implemented with an enum that does not contain any members:
public enum Never {} Copy the code
-
Swift is very rigorous in distinguishing between the various “none” types. In addition to nil and Never, there is also Void, which is another way of writing a tuple:
public typealias Void =(a)Copy the code
Swift makes a careful distinction between “something that doesn’t exist” (nil), “something that exists and is empty” (Void), and “something that can’t happen” (Never).
-
The Guard is an explicit model that implies that we “continue only if the conditions hold.” The Swift compiler also checks to see if you have exited the current scope in the Guard block, and if not, you get a compilation error. Since compiler help is available, choose to use Guard whenever possible, even if it works fine.
Optional chain
-
In Objective-C, nothing happens when you send a message to nil. In Swift we achieve the same effect by “optional chaining” :
delegate?.callback() Copy the code
Nil merge operator
- And the | | operator,??The operator uses short circuiting to evaluate. When we use
l ?? r
, only ifl
When is nil,r
Is going to be evaluated. This is because @Autoclosure is used as the second argument in the function declaration of the operator
Use optional values in string interpolation
-
Swift?? Operator does not support operations with type mismatches on both sides. Just for the special purpose of using optional values in string interpolation, add a?? The operators are also simple
infix operator ?????: NilCoalescingPrecedence public func ????? <T>(optional: T? , defaultValue:@autoclouse() - >String) - >String { switch optional { case let value?:return String(describing: value) case nil: return defaultValue() } } Copy the code
Optional value map
Optional.map
It’s very similar to the map method used in arrays and other sequences. But unlike operating on a series of values in a sequence, optional valuesmapOnly one value is operated on, and that is the possible value in the optional value. You can think of the optional value as a set of zero or one values, somapEither it doesn’t do anything with zero values, or it converts them when it does.
Flatmap optional values
let stringNumbers = ["1"."2"."3"."foo"]
let x = stringNumbers.first.map { Int($0)}// Optional(Optional(1))
Copy the code
FlatMap flattens the result into a single optional value. So y is going to be of type Int, right? :
let y = stringNumbers.first.flatMap { Int($0)}// Optional(1)
Copy the code
Use compactMap to filter nil
- CompactMap – a map that filters out those nil values and unpacks non-nil values.
Optional value judgment, etc
- When we use a non-optional value, Swift will always “upgrade” it to an optional value if we need to match it to an optional value type.
Optional value comparison
- Now, if you want to do anything other than equality between the optional values, you need to unpack them first, and then explicitly indicate what to do with nil.
Timing of forced unpacking
You can use an exclamation point when you’re sure that something you have can’t possibly be nil, and you want the program to just die if it’s accidentally nil.
Improved error messages for forced unpacking
-
Add a!!!!! Operator, which combines forced unpacking with a more descriptive error message that will also be printed when the program exits unexpectedly:
infix operator !!!!! func !!!!! <T>(wrapped: T? ,failureText:@autoclouse() - >String) - >T { if let x = wrapped { return x } fatalError(failureText()) } Copy the code
let s = "foo" let i = Int(s) !!!!! "Expecting integer, got \"\(s)\"" Copy the code
Assertions are made in the debug release
-
Implement an exclamation point! ? Operator — Define this operator to assert failed unpacks and replace the value with the default in releases where the assertion does not trigger:
infix operator ! ? func ! ? <T: ExpressibleByIntegerLiteral> (warpped: T? , failureText:@autoclouse() - >String) - >T { assert(wrapped ! = nil, failureText()) return wrapped ?? 0 } Copy the code
Now, the following code will trigger the assertion at debug time, but print 0 in the release:
let s = "20" let i = Int(s) ! ? "Expecting integer, got \"\(s)\"" Copy the code
-
There are three ways to suspend an operation. First, fatalError will accept a message and unconditionally stop the operation. The second option is to use assert to check the condition, and when the condition results in false, stop execution and output information. In a release, the assert is removed, which means the condition is not detected and the operation is never suspended. The third way is to use precondition, which has the same interface as assert but is not removed in the release, that is, execution is stopped as long as the condition is determined to be false.
Implicit unpacking optional values
Why use implicit optional values?
Reason 1: For now, we might need to go into Objective-C and call code that doesn’t check for the return; Or you might call a C library that is not annotated for Swift.
Reason 2: Because a value is nil only briefly, after a certain period of time, it is never nil again. The most common situation is two-phase initialization.
Implicit optional value behavior
While implicitly unpacked optional values behave like non-optional values, you can still use optional chains, nil merge, if let, map or compare them to nil, all of which are the same:
var s: String! = "Hello"
s?.isEmpty // Optional(false)
if let s = s { print(s) } // Hello
s = nil
s ?? "Goodbye" // Goodbye
Copy the code