The introduction
Continue learning Swift documentation, starting from the previous chapter: We started with the basics of Swift, but now we will start with the basics in detail. Don’t think that the basics are not important, it is essential to master a development language. Due to the long space, here is a section to record, next, let’s begin!
If you already know the basics of Swift, see the next section: Basic Operations
basis
Swift is a new programming language developed for iOS, macOS, watchOS and tvOS. However, based on your experience developing with C and Objective-C, you will be familiar with many parts of Swift.
Swift provides its own version of all the basic C and Objective-C types, including Int for integer, Double and Float for floating-point, Bool for Boolean, and String for text data. Swift also provides powerful versions of the three main Collection Types, Array, Set and Dictionary, defined in Collection Types.
Like C, Swift uses variables to store and reference values by an identity name. Swift also makes extensive use of variables whose values cannot be changed. These are called constants and are much more powerful than constants in C. Using constants in Swift makes code safer and cleaner when you’re dealing with values that don’t need to change.
In addition to the familiar types, Swift introduces advanced types not found in Objective-C, such as tuples. Tuples enable you to create and pass groups of values. You can use tuples to return multiple values in a function as a single compound value.
Swift also introduces optional types to handle missing values. The optional values either say “there is a value, which is equal to x” or “there is no value at all.” Using optional values is similar to using nil for Pointers in Objective-C, but they apply to any type, not just classes. Option is not only safer and more expressive than nil Pointers in Objective-C, but is at the heart of many of Swift’s most powerful features.
Swift is a type-safe language, which means that it helps you clearly understand the types of values your code can use. If part of your code requires a String, type safety prevents you from mistakenly passing it as an Int. Similarly, type safety prevents you from accidentally passing optional Strings to code snippets that require non-optional strings. Type safety helps you catch and fix bugs early in the development process.
1 Constants and variables
Constants and variables will name (such as maximumnumberoflogintries or welcomeMessage) with a specific type of value, such as the number 10 or string “Hello”). The value of a constant cannot be changed once set, while a variable can be set to a different value in the future.
1.1 Define constants and variables
Constants must be declared before they can be used. You can use the let keyword to declare constants and the var keyword to declare variables. Here is an example of how constants and variables can be used to track the number of login attempts:
let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
Copy the code
This code means: “define a maximumNumberOfLoginAttempts constants, and assign a value to 10. Define a currentLoginAttempt variable and assign it to 0.”
In this example, a constant is declared for the maximum number of logins allowed, because the maximum has not been modified. The current login attempt counter is declared as a variable because the value must be incremented after each failed login attempt.
You can declare multiple constants and variables on a single line, separated by commas:
Var x = 0.0, y = 0.0, z = 0.0Copy the code
Note that if the value stored in your code does not change, always declare it as a constant using the let keyword. Variables are only used to store values that need to be changed.
1.2 Type Annotations
When you declare constants and variables, you can add types so that you know what type of value you want to store. Write type comments by following the name of a constant or variable with a colon, a space, and the name of the type to be used.
This example provides a type comment for a variable called welcomeMessage to indicate that it can store string values:
var welcomeMessage: String
Copy the code
The colon in the declaration means “… Of the type of…” , so the above code can be understood as:
“Define a string variable called welcomeMessage.”
The phrase “of type String” means “any String value can be stored.” Think of it as meaning “type of thing” (or “kind of thing”) that can be stored.
The welcomeMessage variable can be set to any string value:
welcomeMessage = "Hello"
Copy the code
You can define multiple related variables of the same type in a single line, separated by commas, with a type comment following the last variable name:
var red, green, blue: Double
Copy the code
In practice, you rarely need to write type annotations. If you provide an initial value when defining a constant or variable, Swift can almost always infer the type used by that constant or variable, as described in type safety and type inference. In the welcomeMessage example above, no initial value is provided, so the type of the welcomeMessage variable is specified through the type annotation, not inferred from the initial value.
1.3 Naming constants and variables
Constant and variable names support almost all characters, including Unicode characters:
letPI = 3.14159letHello =Hello world.
let🐶 🐮 ="dogcow"
Copy the code
Constant variable names cannot contain Spaces, mathematical symbols, arrows, private Unicode scalar values, or **- and box-drawing** characters. Nor can it begin with a number, although numbers can be included elsewhere in the name.
Once a constant or variable of a certain type is declared, it cannot be declared with the same name or change the type it stores. Although you can change a constant to a variable or change a variable to a constant.
Note that if you need to give a constant or variable the same name as the reserved Swift keyword, use backquotes (‘) when using that keyword as the name. However, avoid using keywords as names unless you have no alternative.
You can change the value of an existing variable to a value of another compatible type, for example:
var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcome is now "Bonjour!"
Copy the code
Unlike a variable, a constant cannot be changed once its value has been set. An error will be reported if it is forced to change:
let languageName = "Swift"
languageName = "Swift++"
// This is a compile-time error: languageName cannot be changed.
Copy the code
1.4 Printing constants and variables
Separator :terminator we can use the **print(_:separator:terminator:)** function to print constants or variables:
print(friendlyWelcome)
// Prints "Bonjour!"
Copy the code
The **print(:separator:terminator:)** function is a global function that prints one or more values in the appropriate output. In Xcode, for example, the **print(:separator:terminator:)** function prints the printed value on the Xcode console. Separator and terminator have default values, so you can ignore them when calling this function. By default, the function terminates the line it prints by adding a newline character. To print a value without a newline, pass an empty string as a terminator — for example: print(someValue, terminator: “”). For information about parameters with Default Values, see Default Parameter Values.
Swift uses string interpolation to include the name of a constant or variable as a placeholder in a longer string and prompts Swift to replace it with the current value of that constant or variable. Enclose the name in parentheses, preceded by a backslash escape:
print("The current value of friendlyWelcome is \(friendlyWelcome)")
// Prints "The current value of friendlyWelcome is Bonjour!"
Copy the code
Note that all the options available for String Interpolation are described in String Interpolation.
2 comments
Use comments to include non-executable text in your code as a prompt or reminder to yourself. The Swift compiler ignores comments when the code is compiled.
The comments in Swift are very similar to those in C. Single-line comments with double slashes (//) :
// This is a comment.
Copy the code
Multiple lines can only be used with /*… * / :
/* This is also a comment
but is written over multiple lines. */
Copy the code
Unlike multi-line comments in C, multi-line comments in Swift can be nested within other multi-line comments. You can write nested comments by starting a multi-line comment block and then starting a second multi-line comment in the first comment block. Then close the second block, then close the first block:
/* This is the start of the first multiline comment.
/* This is the second, nested multiline comment. */
This is the end of the first multiline comment. */
Copy the code
Nested multi-line comments enable you to quickly and easily comment out large chunks of code, even if the code already contains multi-line comments.
3 a semicolon
Unlike many other languages, Swift does not require a semicolon at the end of a statement, although you may wish to. However, when multiple statements are written on the same line, semicolons are needed to separate them:
let cat = "🐱"; print(cat)
// Prints "🐱"
Copy the code
4 integer
Integers are integers without fractions, such as 42 and -23. Integers can be signed (positive, zero, or negative) or unsigned (positive or zero).
Swift provides signed and unsigned integers in 8-bit, 16-bit, 32-bit, and 64-bit formats. These integers follow a naming convention similar to C, where 8-bit unsigned integers are of type UInt8 and 32-bit signed integers are of type Int32. As with all types in Swift, the names of these integer types are uppercase.
4.1 Integer Range
You can access the minimum and maximum values for each integer type and its min and Max attributes:
let minValue = UInt8.min // minValue is equal to 0, and is of type UInt8
let maxValue = UInt8.max // maxValue is equal to 255, and is of type UInt8
Copy the code
The values of these properties have numeric types of appropriate size (such as UInt8 in the example above) and can therefore be used with other values of the same type in expressions.
4.2 the Int
In most cases, you don’t need to select integers of a specific size in your code. Swift provides an additional integer type Int, which is the same size as the local word on the current platform:
- On 32-bit platforms, Int is the same size as Int32.
- On 64-bit platforms, Int is the same size as Int64.
Integers are defined as ints unless you need to deal with an integer of a particular size. This contributes to code consistency and interoperability. Even on 32-bit platforms, Int can store values between -2,147,483,648 and 2,147,483,647, a range that can be used for most integers.
4.3 UInt
Swift also provides an unsigned integer, UInt, which is the same size as the local word on the current platform:
- On 32-bit platforms, the UInt size and UInt32 are the same.
- On 64-bit platforms, the UInt size is the same as that of UInt64.
Note that UInt is only used if you specifically need an unsigned integer type of the same size as the platform’s local word. If not, Int is preferred, even if the value to be stored is known to be non-negative. Consistent use of Int for integer values helps code interoperability, avoids conversions between different numeric types, and matches integer type inference, as described in type safety and type inference.
5 Floating- A Floating point number
Floating point numbers are numbers with decimal parts, such as 3.14159, 0.1, and -273.15.
Floating point types can represent a wider range of values than integers and can store larger or smaller numbers than integers. Swift provides two signed floating-point types:
- Double represents a 64-bit floating point number.
- Float represents a 32-bit floating point number.
Note that Double is accurate to at least 15 decimal digits, while Float is accurate to only 6 decimal digits. The appropriate floating point type to use depends on the nature and range of values you want to use in your code. If both types are appropriate, Double is preferred.
6 Type security and type inference
Swift is a type-safe language that encourages you to be explicit about the types of values your code can use. If part of the code requires a string, you cannot pass it as an Int by mistake.
Because Swift is type safe, it performs type checking when compiling code and marks any mismatches as errors. This enables you to catch and fix bugs early in the development process.
Type checking can help you avoid errors when you use different types of values. However, this does not mean that you must specify the type of every constant and variable declared. If the type of the desired value is not specified, Swift uses type inference to figure out the appropriate type. Type inference enables the compiler to automatically infer the type of a particular expression by examining the supplied value when compiling code.
Thanks to type inference, Swift requires far fewer type declarations than languages such as C or Objective-C. Constants and variables are still explicitly typed, but most of the work of specifying their types has already been done for you.
Type inference is particularly useful when declaring constants or variables with initial values. This is usually done by assigning a literal value (or literal) to a constant or variable when it is declared. (Literal values are those that appear directly in the source code, such as 42 and 3.14159 in the following examples.)
For example, if you assign a new constant to 42 without specifying what type it is, Swift deduces that you want the constant to be an integer because you initialized it with a number that looks like an integer:
let meaningOfLife = 42
// meaningOfLife is inferred to be of type Int
Copy the code
Similarly, if you do not specify the type of a floating-point literal, Swift deduces that you want to create a Double:
let// what does the man say about the woman? // What does the man say about the womantype Double
Copy the code
Swift always chooses Double(rather than Float) when inferring floating-point types.
If you combine integer and floating-point literals in an expression, Double is inferred from the context:
letAnotherPi = 3 + 0.14159 // anotherPi is also inferred to be oftype Double
Copy the code
Literal 3 has no explicit type of its own, so the appropriate output type, Double, can be inferred from the floating-point literal as part of the addition.
7 Numeric literal
Integer literals can be written as:
- An unprefixed decimal number
- A binary number prefixed with 0b
- Octal number with 0O prefix
- A hexadecimal number prefixed with 0x
All of these literals have a decimal value of 17:
let decimalInteger = 17
letBinaryInteger = 0b10001 // 17 binaryletOctalInteger = 0o21 // 17 octalletHexadecimalInteger = 0x11 // 17 HexadecimalCopy the code
Floating-point literals can be decimal (without a prefix) or hexadecimal (with a 0x prefix). They must have a number (or hexadecimal number) on both sides of the decimal point. Decimal floating-point numbers can also have an optional exponent, represented by either upper or lower case e; Hexadecimal floating-point numbers must have an exponent, represented by either a uppercase or lowercase p.
For a decimal with an exponent of exp, multiply the base by 10exp:
- 1.25E2 means 1.25 x 102, or 125.0.
- 1.25E-2 means 1.25 x 10-2, or 0.0125.
For a hexadecimal number with an exponent of exp, multiply the base by 2exp:
- 0xFp2 means 15 x 22, or 60.0.
- 0xFP-2 means 15 x 2-2, or 3.75.
All of these floating-point literals have a decimal value of 12.1875:
letDecimalDouble = 12.1875letExponentDouble = 1.21875 e1let hexadecimalDouble = 0xC.3p0
Copy the code
Numeric literals can contain additional formats to make them easier to read. Both integers and floating-point numbers can be padded with extra zeros, and can include underscores to enhance readability. Neither format affects the base value of the text:
letPaddedDouble = 000123.456let oneMillion = 1_000_000
let_000_1 justOverOneMillion = 1 _000_000. 000Copy the code
8 Numeric type conversion
Use Int for all generic integer constants and variables in your code, even if they are known to be nonnegative. Using the default integer type in everyday situations means that integer constants and variables are immediately interoperable in code and will match the inferred types of integer literal values.
Use other integer types only when the task at hand specifically requires them, because external sources have explicitly resized the data, or for performance, memory usage, or other necessary optimizations. Using explicit sized types in these cases helps catch any unexpected value overflows and implicitly record the nature of the data being used.
8.1 Integer Conversion
The range of numbers that can be stored in integer constants or variables is different for each numeric type. Int8 constant or variable can store numbers between -128 and 127, while UInt8 constant or variable can store numbers between 0 and 255. When your code compiles, a number that does not fit a constant or variable of the size integer type is reported as an error:
let cannotBeNegative: UInt8 = -1
// UInt8 cannot store negative numbers, and so this will report an error
let tooBig: Int8 = Int8.max + 1
// Int8 cannot store a number larger than its maximum value,
// and so this will also report an error
Copy the code
Because each numeric type can store a different range of values, numeric type conversions must be selected on a case-by-case basis. This optional approach prevents hidden conversion errors and helps show type conversion intent explicitly in your code.
To convert one particular numeric type to another, a new number of the desired type can be initialized using an existing value. In the example below, constant 2000 is of type UInt16, while constant 1 is of type UInt8. They can’t add directly because they’re not of the same type. Instead, this example calls UInt16(one) to create a new UInt16 that initializes as one and uses this value instead of the original one:
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
Copy the code
Since both sides of the addition are now UInt16 types, the addition is allowed. The output constant (twoThousandAndOne) is inferred to be of type UInt16 because it is the sum of two UInt16 values.
SomeType(ofInitialValue) is the default method for calling an initializer of type Swift and passing in an initial value. In the background, UInt16 has an initializer that accepts the value of the UInt8, so this initializer is used to generate a new UInt16 from the existing UInt8. However, you can’t pass in any type here — it has to be the type of UInt16 that provides the initializer. Extensions include extending existing types to provide initializers that accept new types, including your own type definitions.
8.2 Integer and floating point conversion
Conversions between integer and floating point types must be unambiguous:
let three = 3
letPointOneFourOneFiveNine = 0.14159let// what does the man say about the woman? // What does the man say about the woman? // What does the man say about the womantype Double
Copy the code
Here, the value of the constant 3 is used to create a new value of type Double, so that both sides of the addition are of the same type. If this transformation is not performed, the addition is not allowed.
Floating-point to integer conversions must also be unambiguous. Integer types can be initialized with either a double-value or floating-point value:
let integerPi = Int(pi)
// integerPi equals 3, and is inferred to be of type Int
Copy the code
When a new integer value is initialized in this way, the floating-point value is always truncated. This means that 4.75 becomes 4 and -3.9 becomes -3.
Note that the rules for combining numeric constants and variables are different from the rules for combining numeric literals. The literal value 3 can be added directly to the literal value 0.14159, because numeric literals themselves do not have explicit in and of types. Their types are inferred only when evaluated by the compiler.
9 Type Aliases
A type alias defines an alternative name for an existing type. Type aliases can be defined through the TypeAlias keyword.
Type aliases are useful when you want to refer to an existing type by a more contextually appropriate name, such as when dealing with data of a particular size from an external source:
typealias AudioSample = UInt16
Copy the code
Once a type alias is defined, you can use the alias anywhere, where the original name might be used:
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound is now 0
Copy the code
Here, AudioSample is defined as an alias for UInt16. Because it is an alias, call audiosample. min which is also called uint16.min, as maxAmplitudeFound is 0.
10 Boolean value
Swift has a basic Boolean type called Bool. Boolean values are called logical values because they can only be true or false. Swift provides two Boolean constant values true and false:
let orangesAreOrange = true
let turnipsAreDelicious = false
Copy the code
The types orangesAreOrange and turnipsAreDelicious have been inferred to be Bool because they are initialized with Boolean literal values. As with Int and Double above, you do not need to declare constants or variables as Bool if they are set to true or false when they are created. Type inference helps make Swift code more concise and readable when Swift initializes a constant or variable with a value of another known type.
Booleans are particularly useful when dealing with conditional statements, such as the if statement:
if turnipsAreDelicious {
print("Mmm, tasty turnips!")}else {
print("Eww, turnips are horrible.")
}
// Prints "Eww, turnips are horrible."
Copy the code
Conditional statements, such as the if statement, are covered in more detail in control flow.
Swift’s type-safety prevents non-Booleans from being replaced by Bool. The following example reports a compile-time error:
let i = 1
if i {
// this example will not compile, and will report an error
}
Copy the code
However, here is another example that works:
let i = 1
if i == 1 {
// this example will compile successfully
}
Copy the code
The comparison of I == 1 results in a Bool, so the second example passes the type check. Comparisons like I == 1 are discussed in the basic operators.
As with other type security examples in Swift, this approach avoids unexpected errors and ensures that the intent of a particular code segment is always clear.
11 yuan group
Tuples group multiple values into a single compound value. The values in a tuple can be of any type; they do not have to be of the same type as each other.
In this case, (404, “Not Found”) is a tuple describing the HTTP status code. The HTTP status code is a special value returned by the Web server when requesting a Web page. If the page you requested does Not exist, it will return a 404 Not Found status code.
let http404Error = (404, "Not Found")
// http404Error is of type (Int, String), and equals (404, "Not Found")
Copy the code
A tuple combines an Int and a string to provide two separate values for the HTTP status code: a number and a readable description. It can be described as a “tuple of type (Int, String)”.
You can create tuples based on any permutation of types, and they can contain as many different types as you want. There is nothing to prevent you from having a tuple of type (Int, Int, Int) or (String, Bool), or any other substitution you need.
You can break up the contents of a tuple into individual constants or variables and then access them as usual:
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
// Prints "The status code is 404"
print("The status message is \(statusMessage)")
// Prints "The status message is Not Found"
Copy the code
If you only need the values of a few tuples, ignore part of the tuple with an underscore (_) when you decompose the tuple:
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// Prints "The status code is 404"
Copy the code
Alternatively, use index numbers starting at 0 to access individual element values in a tuple:
print("The status code is \(http404Error.0)")
// Prints "The status code is 404"
print("The status message is \(http404Error.1)")
// Prints "The status message is Not Found"
Copy the code
When a tuple is defined, you can name individual elements in the tuple:
let http200Status = (statusCode: 200, description: "OK")
Copy the code
If you name elements in a tuple, you can access the values of those elements using the element names:
print("The status code is \(http200Status.statusCode)")
// Prints "The status code is 200"
print("The status message is \(http200Status.description)")
// Prints "The status message is OK"
Copy the code
Tuples are particularly useful as return values of functions. A function that tries to retrieve a Web page may return a tuple of type (Int, String) to describe the success or failure of the page retrieval. By returning a tuple with two different types of values, a function provides more useful information about its results than if it could only return a single value of a single type. For more information, see Functions with Multiple Return values.
Note that tuples are useful for simple groups of related values. They are not suitable for creating complex data structures. If the data structure can be more complex, model it as a class or structure rather than a tuple. For more information, see Structures and Classes.
12 optional values
Use optional values in cases where none may exist. Optional indicates two possibilities: either there is a value that you can expand to access, or there is no value at all.
Note that the concept of optional values does not exist in C or Objective-C. The closest thing you can get in Objective-C is the ability to return nil from a method that would otherwise return an object, and nil means “missing a valid object.” However, this applies only to objects, not structures, primitive C types, or enumerated values. For these types, objective-C methods usually return a special value (such as NSNotFound) to indicate that there is no value. The caller of this hypothetical method knows that there is a particular value to test and remembers to check it. Swift’s optional values allow you to indicate whether any type of value exists, without requiring a special constant.
Here is an example of how to use optional values to handle missing values. Swift’s Int type has an initializer that attempts to convert a string value to an Int value. However, not every string can be converted to an integer. The string “123” can be converted to a numeric value of 123, but the string “hello, world” has no obvious numeric value to convert.
The following example uses an initializer to try to convert a string to an Int:
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber is inferred to be of type "Int?", or "optional Int"
Copy the code
Because the initializer may fail, it returns an optional integer instead of an integer. The question mark indicates that the value it contains is optional, which means that it may contain some Int values or no value at all. It can’t contain anything else, like Bool values or String values. Either integer or nothing)
12.1 nil
Set an optional variable to a no-value state by assigning the special value nil:
var serverResponseCode: Int? = 404
// serverResponseCode contains an actual Int value of 404
serverResponseCode = nil
// serverResponseCode now contains no value
Copy the code
Note that you cannot use nil for non-optional constants and variables. If a constant or variable in your code needs to handle missing values under certain conditions, always declare it as an optional value of the appropriate type.
If an optional variable is defined and no value is set to it, its value defaults to nil:
var surveyAnswer: String?
// surveyAnswer is automatically set to nil
Copy the code
Notice the difference between nil in Swift and OC, where nil is a pointer to an object that doesn’t exist. Nil in Swift is not a pointer — it’s a missing value of some type. Options of any type can be set to nil, not just object types.
12.2 If Statement and Forced Decompacking
By comparing optional and null values, you can use the if statement to determine whether an optional value contains a value. You can use the “equal” operator (==) or the “not equal” operator (! =) performs the comparison.
If the optional value has a value, it is not considered nil:
ifconvertedNumber ! = nil {print("convertedNumber contains some integer value.")
}
// Prints "convertedNumber contains some integer value."
Copy the code
Once you determine that the optional value has a value, you can do this by adding an exclamation mark (!) to the end of the optional name. To access its underlying value. An exclamation point effectively says: “I know that this optional value must have a value; Please use it. This is called forced unpacking of optional values:
ifconvertedNumber ! = nil {print("convertedNumber has an integer value of \(convertedNumber!) .")
}
// Prints "convertedNumber has an integer value of 123."
Copy the code
To learn more about If statements, see Control Flow.
Pay attention to try to use! Accessing optional values that do not exist will trigger a runtime error. Make sure an optional contains a non-nil value before using it! Force open its value.
12.3 Optional Value Binding
You can use the optional binding to find out if the optional contains a value, and if so, make that value available as a temporary constant or variable. Optional bindings can be used with if and while statements to examine a value in an optional statement and extract that value as a constant or variable as part of a single operation. The if and while statements are described in more detail in the control flow.
Use the If statement to write an optional binding as follows:
if let constantName = someOptional {
statements
}
Copy the code
You can override the possibleNumber example in the Optionals section to use optional bindings instead of forced unpacking:
if let actualNumber = Int(possibleNumber) {
print("The string \"\(possibleNumber)\" has an integer value of \(actualNumber)")}else {
print("The string \"\(possibleNumber)\" could not be converted to an integer")
}
// Prints "The string "123" has an integer value of 123"
Copy the code
This code says: “If the optional integer returned by Int(possibleNumber) contains a value, set a new constant named actualNumber to the value contained in the optional integer.”
If the conversion succeeds, you can use the actualNumber constant in the first branch of the If statement. It is already initialized with the values included in the optional, so it is not needed! Suffix to access its value. In this case, the actualNumber is simply used to print the result of the transformation.
Optionally bound constants and variables can be used. If you want to operate on the value of actualNumber in the first branch of an If statement, you can write If var actualNumber, and the value contained in the optional statement will be available as a variable rather than a constant.
You can include as many optional bindings and Boolean conditions in a single if statement, separated by commas. The condition of the entire If statement is considered false If any value in the optional binding is nil or If any Boolean condition is false. The following if statement is equivalent:
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
// Prints "4 < 42 < 100"
if let firstNumber = Int("4") {
if let secondNumber = Int("42") {
if firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
}
}
// Prints "4 < 42 < 100"
Copy the code
Note that constants and variables created using optional bindings in an IF statement can only be used in the body of an if statement. Instead, constants and variables created using guard statements can be used in lines of code following guard statements, as described in Early Exit.
12.4 Implicit Unpacking This parameter is optional
As mentioned above, optional values indicate that a constant or variable is allowed to have “no value”. Optional values can be checked for existence with an if statement, or conditionally unwrapped with an optional binding to access optional values if they exist.
Sometimes programmatically, the optional value will always have a value after the first setting. In these cases, the operation of removing check and unpacking is effective when accessing optional values, because it always safely assumes a value.
These types of optional values are defined as implicit unpack optional values. By placing an exclamation mark (String!) after the type you want to make optional. Instead of a question mark (String?) To write optional implicit unpacking. Do not place an exclamation mark after an optional name. Instead, place an exclamation mark after the declaration of an optional type.
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation point
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // no need for an exclamation point
Copy the code
(Note: This is finally clear? And! To learn a language, start with the official documentation.
You can think of optional implicit unpacking as allowing optional objects to force unpacking if needed. When you use implicit unwrapping optional values, Swift first tries to use them as plain optional values; If it is not available as an optional value, Swift forces the value on. In the code above, the optional value assumedString is forcibly unwrapped before assigning its value to implicitString, because implicitString has an explicit, non-optional string type. In the code below, optionalString has no explicit type, so it is a normal optional type.
let optionalString = assumedString
// The type of optionalString is "String?" and assumedString isn't force-unwrapped.
Copy the code
If an optional implicit unpack is nil and you try to access its wrapper value, you will fire a runtime error. The result is exactly the same as placing an exclamation mark after a plain optional that contains no value.
You can check if an optional implicit unpack is nil, just like you check for a normal optional:
ifassumedString ! = nil {print(assumedString!)
}
// Prints "An implicitly unwrapped optional string."
Copy the code
You can also implicitly unpack the optional value using the optional value binding, checking and unpacking its value in a separate statement:
if let definiteString = assumedString {
print(definiteString)
}
// Prints "An implicitly unwrapped optional string."
Copy the code
Note that optional value bindings are not used to implicitly unpack optional values when a variable may later become nil. If you need to check nil values for the lifetime of a variable, always use the normal optional type.
13 Error Handling
Use error handling to respond to error conditions that your program may encounter during execution.
In contrast to optional values, which can use the presence or absence of a value to convey the success or failure of a function, error handling allows you to determine the root cause of the failure and, if necessary, propagate the error to another part of the program.
When a function encounters an error condition, it throws an error. The caller of the function can then catch the error and respond appropriately.
func canThrowAnError() throws {
// this function may or may not throw an error
}
Copy the code
A function indicates that it can throw an error by including the throws keyword in its declaration. When calling a function that might throw an error, the try keyword should be added before the expression.
Swift automatically propagates errors out of the current scope until they are processed by the catch clause.
do {
try canThrowAnError()
// no error was thrown
} catch {
// an error was thrown
}
Copy the code
The DO statement creates a new containment scope, allowing errors to be propagated to one or more catch clauses.
Here is an example of how error handling can be used to respond to different error conditions:
func makeASandwich() throws {
// ...
}
do {
try makeASandwich()
eatASandwich()
} catch SandwichError.outOfCleanDishes {
washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
buyGroceries(ingredients)
}
Copy the code
In this case, the makeASandwich() function throws an error if there are no clean plates or any ingredients are missing. Because makeASandwich() can throw an error, the function call is wrapped in a try expression. By wrapping the function call in a DO statement, any errors thrown are propagated to the supplied catch clause.
If no errors are thrown, the eatASandwich() function is called. If an error is thrown and it matches sandwichError. In the case of outOfCleanDishes, the washDishes() function is called. If an error is thrown and it matches sandwichError. In the missingcomponents case, the buylost (_:) function is called with the relevant [String] value captured in catch mode.
Throwing, catching, and propagating errors are covered in more detail in error handling.
14 Assertions and prerequisites
Assertions and preconditions are checks that occur at run time. Use them to ensure that the necessary conditions are met before executing any other code. If a Boolean value in an assertion or prerequisite is true, code execution continues as usual. If the condition evaluates to false, the current state of the program is invalid; Code execution ends and the application terminates.
Assertions and prerequisites can be used to express assumptions and expectations when coding, so they can be part of the code. Assertions help you find errors and incorrect assumptions during development, while preconditions help you detect problems in production.
In addition to verifying your expectations at run time, assertions and prerequisites also become useful forms of documentation in your code. Unlike the error conditions discussed in error handling above, assertions and preconditions are not used for recoverable or expected errors. Failed assertions cannot be caught because failed assertions or prerequisites indicate invalid program state.
Using assertions and prerequisites is no substitute for designing code in a way that makes invalid conditions less likely. However, using them to enforce valid data and state causes the application to terminate predictably when an invalid state occurs, and helps make the problem easier to debug. Stopping execution immediately when an invalid state is detected also helps limit the damage caused by that invalid state.
The difference between assertions and preconditions is when they are checked: Assertions are only checked in debug builds, but preconditions are checked in both debug and production builds. In production builds, conditions in assertions are not evaluated. This means that you can use any number of assertions during development without affecting performance in production.
14.1 Debugging with Assertions
Assertions are written by calling the assert(::file:line:) function from the Swift standard library. The function is passed an expression that evaluates to true or false and a message that is displayed if the result of the condition is false. Such as:
let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// This assertion fails because -3 is not >= 0.
Copy the code
In this case, if the value of age >= 0 is true (that is, the value of age is non-negative), the code continues to execute. If the value of age is negative, as shown in the code above, then age >= 0 evaluates to false, the assertion fails, and the application terminates.
You can omit the assertion message — for example, when it simply repeats the condition in prose.
assert(age >= 0)
Copy the code
If the code has already checked the condition, the assertionFailure(_:file:line:) function can be used to indicate that the assertion failed. Such as:
if age > 10 {
print("You can ride the roller-coaster or the ferris wheel.")}else if age >= 0 {
print("You can ride the ferris wheel.")}else {
assertionFailure("A person's age can't be less than zero.")}Copy the code
14.2 Performing Prerequisites
Use preconditions whenever the condition can be false, but must be true for the code to continue. For example, use prerequisites to check that subscripts are out of range, or that the function passes valid values.
You write one precondition by calling precondition(_:_:file:line:) function. The function is passed an expression that evaluates to true or false and a message that is displayed if the result of the condition is false. Such as:
// In the implementation of a subscript...
precondition(index > 0, "Index must be greater than zero.")
Copy the code
You can also call the preconditionFailure(_:file:line:) function to indicate that a failure has occurred — for example, if the switch’s default case is followed, but all valid input data should be processed by one of the other cases of the switch.
Notice that if you compile in unchecked mode (-ounchecked), the preconditions are not checked. The compiler assumes that the preconditions are always true and optimizes the code accordingly. However, regardless of the optimization Settings, the fatalError(_:file:line:) function always stops execution.
You can use the fatalError(_: File :line:) function during prototyping and early development to create stubs for functionality that has not yet been implemented by writing fatalError(” not implemented “) as a stub implementation. Because, unlike assertions or prerequisites, fatal errors are never optimized, you can be sure that execution will always stop if a stub implementation is encountered.
conclusion
Through this chapter, you can learn some basic knowledge of Swift and its unique functions:
- How to define constants and variables
- How to add a comment: Similar to OC, but with a **”””** to comment multiple lines
- The semicolon is used for writing multiple lines of code
- Integer, floating point, and Boolean values
- Swift’s unique type security and type inference: This is really useful and increases development efficiency
- Numeric literal and numeric type conversion
- Type the alias
- tuples
- Optional values:? And! , and the optional binding if-let
- Error handling: try-catch and do-try-catch
- Assertions and prerequisites I won’t go into all the details here, but you can find the answers above. Finally, if you like it, you can give a star. Your support is my motivation!
The next chapter: Basic operations
Reference: Swift – The Basics