❝
This is a Case Study summary by Cara, a full-stack program of our group. It is a 10,000-word long article. Each knowledge point corresponds to a demo, combining theory with practice, and the explanation is in place. It is suggested that students collect, share and forward three links.
❞
Swift is introduced
Swift is a new language for iOS, macOS, watchOS and tvOS applications. Swift is a safe, fast and interactive programming language. Swift supports Code Preview (PlayGrounds), a feature that allows programmers to run Swift code and see the results in real time without having to compile and run the application.
Swift avoids a number of common programming errors by adopting a modern programming model:
- Variables are always initialized before use.
- Check for array index out of range errors.
- Check whether an integer overflow occurs.
- Optional values ensure that nil values are explicitly handled.
- Memory is automatically managed.
- Error handling allows recovery from unexpected failure control.
Based on some
Constants and variables
Declare constants and variables, constants and variables must be declared before use, use let to declare constants, use var to declare variables. Example:
let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
// Type annotationsvar welcomeMessage: String
Copy the code
annotation
Single-line comments double forward slashes (//), multi-line comments (/* multi-line */). Swift multiline comments can be nested within other multiline comments. Example:
// This is a comment
/* This is also a comment,But it's multiple rows of */
/* This is the beginning of the first multi-line comment/* This is the second nested multi-line commentThis is the end of the first multi-line comment */Copy the code
A semicolon
Swift does not force you to use a semicolon (;) at the end of each statement. . Multiple independent statements written on the same line must be separated by semicolons.
let cat = "🐱"; print(cat)
/ / output "🐱"Copy the code
Integer, floating point number
The uniform use of ints improves code reusability, avoids conversions between different types of numbers, and matches type inference of numbers. Example:
letMinValue = uint8.min // UInt8 typeletMaxValue = uint8.max // The maxValue is 255 and is the UInt8 typeCopy the code
Type safety and type inference
Swift is a type-safe language, which means that Swift lets you know exactly what type a value is. If you do not explicitly specify a type, Swift uses type inference to select the appropriate type. Int, double). Example:
let meaningOfLife = 42
// meaningOfLife is supposed to be Int
letPI = 3.14159// PI is supposed to be of type DoubleCopy the code
Numeric literals, numeric type conversions
Example:
let decimalInteger = 17
letBinaryInteger = 0b10001 // Binary 17letOctalInteger = 0O21 // Octal 17letHexadecimalInteger = 0x11 // Hexadecimal 17Copy the code
Type the alias
Type aliases define another name for an existing type. You can define a typealias using the typealias keyword. Example:
typealias AudioSample = UInt16
var maxAmplitudeFound = AudioSample.min
MaxAmplitudeFound is now 0Copy the code
Boolean value
Example:
let orangesAreOrange = true
let turnipsAreDelicious = false
Copy the code
tuples
Tuples combine multiple values into a compound value. Values in tuples can be of any type and do not have to be the same type. Example:
let http404Error = (404, "Not Found")
// http404Error is of type (Int, String) and value (404,"Not Found")
Copy the code
Optional type
Use optionals to handle cases where values may be missing. Optional types represent two possibilities: either there is a value, which you can access by parsing the optional type, or there is no value at all. Example:
var serverResponseCode: Int? = 404
// serverResponseCode contains an optional Int value 404serverResponseCode = nil
ServerResponseCode now contains no valueCopy the code
Error handling
Error handling, which deals with error conditions that may be encountered during program execution. Example:
func makeASandwich() throws {
// ...
}
do {
try makeASandwich() eatASandwich() } catch SandwichError.outOfCleanDishes { washDishes() } catch SandwichError.missingIngredients(let ingredients) { buyGroceries(ingredients) } Copy the code
Assertions and prerequisites
Assertions and prerequisites are checks made at run time.
let age = -3
assert(age >= 0, "A person's age cannot be less than zero")
// The assertion is fired because age is < 0Copy the code
Basic operator
Swift supports most of the standard C operators and provides interval operators that ARE not available in C, such as a.. < b or a… B.
Assignment operators, arithmetic operators, combined assignment operators, comparison operators, ternary operators, null-sum operators, interval operators, logical operators
Operators are divided into unary, binary, and ternary operators. The closed interval operator (a… B) Define an interval containing all values from a to b, including a and b. The half-open interval operator (a..
let names = ["Anna"."Alex"."Brian"."Jack"]
let count = names.count
for i in0.. <count { print("\(I + 1) \(names[I])")
}
// The first person is Anna// The second person is called Alex// The third person is Brian// The fourth man is called JackCopy the code
Strings and characters
String literals, string interpolation, counting characters, accessing and modifying strings, substrings, comparing strings
Initializes empty strings, string variability, strings are value types, concatenates strings and characters (+, +=). Using characters, you iterate through the string through a for-in loop, getting the value of each character in the string. String interpolation is a way to build new strings that can contain constants, variables, literals, and expressions. You can insert constants, variables, literals, and expressions into an existing string to form a longer string. Swift provides three ways to compare text values: string character equality, prefix equality, and postfix equality. Example:
// Multiline string literalslet quotation = """
The White Rabbit put on his spectacles. "Where shall I begin,
please your Majesty?" he asked.
"Begin at the beginning," the King said gravely, "and go on till you come to the end; then stop." """ // The following two strings are the samelet singleLineString = "These are the same." let multilineString = """ These are the same. """ // String interpolationlet multiplier = 3 let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)" / / the message is"3 times 2.5 is 7.5" Count the number of charactersvar word = "cafe" print("the number of characters in \(word) is \(word.count)") // Prints "The number of charactersinCafe is 4" var emptyString = ""// Empty string literalVar anotherEmptyString = String() // Initialize method// Both strings are empty and equivalent. let catCharacters: [Character] = ["C"."a"."t"."!"] let catString = String(catCharacters) print(catString) // Print out: "Cat!"Copy the code
Collection types
The Swift language provides three basic collection types, Array, Set, and Dictionary, for storing collection data. An array is a set of ordered data. A set is an unordered set of unduplicated data. A dictionary is an unordered set of key-value pairs.
The variability of collections, Arrays, Sets, collection operations, dictionaries
Arrays use ordered lists to store multiple values of the same type. The same value can appear multiple times in different places in an array. A collection is used to store values of the same type in no definite order. Use collections instead of arrays when the order of collection elements is not important or when you want to ensure that each element occurs only once. Set operations can efficiently perform some basic operations on sets, such as grouping two sets together, determining whether they share elements, or determining whether two sets are fully contained, partially contained, or disjointed. A dictionary is an unordered set that stores relationships between pairs of keys, all of which need to be of the same type, and all of which need to be of the same type. Each value is associated with a unique key, which acts as an identifier for the value data in the dictionary. Example:
/ / collectionvar someInts = [Int]()
print("someInts is of type [Int] with \(someInts.count) items.")
// Print "someInts is oftype(Int) with 0 items."
Var threeDoubles = Array(Repeating: 0.0, count: 3)// threeDoubles is a [Double] array, equivalent to [0.0, 0.0, 0.0] Var anotherThreeDoubles = Array(Repeating: 2.5, count: 3)// anotherThreeDoubles is inferred as [Double], equivalent to [2.5, 2.5, 2.5]var sixDoubles = threeDoubles + anotherThreeDoubles // sixDoubles is deduced as [Double], equivalent to [0.0, 0.0, 0.0, 2.5, 2.5, 2.5] // the enumerated() method traverses the arrayvar shoppingList: [String] = ["Eggs"."Milk"] for (index, value) in shoppingList.enumerated() { print("Item \(String(index + 1)): \(value)") } Copy the code
The control flow
For-in loops, While loops (Repeat-While), conditional statements, control transfer statements, early exit (GUARD), API availability detection
Like an if statement, the execution of Guard depends on the Boolean value of an expression. We can use the guard statement to demand that the condition must be true to execute the code after the guard statement. Unlike an if statement, a Guard statement always has an else clause, and executes the code in the else clause if the condition is not true. Swift has built-in support for checking API availability, and the compiler uses the available information in the SDK to verify that all apis used in our code are available on the deployment target specified by the project. If we try to use an API that is not available, Swift will report an error during compilation. Example:
let names = ["Anna"."Alex"."Brian"."Jack"]
for name in names {
print("Hello, \(name)!")
}
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4)for (animalName, legCount) in numberOfLegs { print("\(animalName)s have \(legCount) legs") } // The general format of a repeat-while looprepeat { statements } while condition // Exit earlyfunc greet(person: [String: String]) { guard let name = person["name"] else { return } print("Hello \(name)!") guard let location = person["location"] else { print("I hope the weather is nice near you.") return } print("I hope the weather is nice in \(location).") } greet(person: ["name": "John"]) // Output "Hello John!"// Output "I hope the weather is nice near you."greet(person: ["name": "Jane"."location": "Cupertino"]) // Output "Hello Jane!"// Output "I hope the weather is niceinCupertino."Copy the code
function
Function definition and call, function parameter and return value, function parameter label and parameter name, function type, nested function
Optional tuple return type. When defining an input or output parameter, precede the parameter definition with the inout keyword. Example:
/ / functionfunc greet(person: String) -> String {
let greeting = "Hello, " + person + "!"
return greeting
}
func greet(person: String, from hometown: String) -> String { return "Hello \(person)! Glad you could visit from \(hometown)." } print(greet(person: "Bill", from: "Cupertino")) // Print "Hello Bill! Glad you could visit from Cupertino." // Optional tuple return typefunc minMax(array: [Int]) -> (min: Int, max: Int)? { if array.isEmpty { return nil } var currentMin = array[0] var currentMax = array[0] for value in array[1..<array.count] { if value < currentMin { currentMin = value } else if value > currentMax { currentMax = value } } return (currentMin, currentMax) } // An implicit return functionfunc greeting(for person: String) -> String { "Hello, " + person + "!" } print(greeting(for: "Dave")) / / print"Hello, Dave! // Parameter labelfunc greet(person: String, from hometown: String) -> String { return "Hello \(person)! Glad you could visit from \(hometown)." } print(greet(person: "Bill", from: "Cupertino")) // Print "Hello Bill! Glad you could visit from Cupertino."Copy the code
closure
Closures are self-contained blocks of functional code that can be passed and used in code. This is similar to Lambdas in some programming languages.
Closure expressions, trailing closures, value capture, closures are reference types, escaping closures (@escaping), automatic closures
If you need to pass a long closure expression as the last argument to a function, it is useful to replace the closure with a trailing closure. Closures can capture constants or variables in the context in which they are defined. Even if the original scope in which these constants and variables were defined no longer exists, the closure can still refer to and modify these values within the closure function body. Example:
// Closure expression syntax{ (parameters) -> return type in
statements
}
// Tag the closurelet digitNames = [ Zero:"Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four". 5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine" ] let numbers = [16, 58, 510] let strings = numbers.map { (number) -> String in var number = number var output = "" repeat { output = digitNames[number % 10]! + output number /= 10 } while number > 0 return output } // The String constant is inferred to be an array of strings, that is, [String]// Its value is ["OneSix"."FiveEight"."FiveOneZero"] / / value capturefunc makeIncrementer(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementer() -> Int { runningTotal += amount return runningTotal } return incrementer } // Automatic closure, delay evaluationvar customersInLine = ["Chris"."Alex"."Ewa"."Barry"."Daniella"] print(customersInLine.count) // Print "5" let customerProvider = { customersInLine.remove(at: 0) } print(customersInLine.count) // Print "5" print("Now serving \(customerProvider())!") // Prints "Now serving Chris!" print(customersInLine.count) // Print "4"Copy the code
The enumeration
Use the enum keyword to create enumerations and put their entire definition in a pair of curly braces.
Enumeration syntax, matching enumeration values with Switch statements, traversal of enumeration members, associated values, primitive values (default), recursive enumeration (indirect)
Swift enumerations can be defined to store associated values of any type, and the associated value type can be different for each enumeration member. Example:
// Enumeration syntaxenum SomeEnumeration {
// Put the enumeration definition here}
enum CompassPoint { case north case south case east case west } enum Planet { case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune } let somePlanet = Planet.earth switch somePlanet { case .earth: print("Mostly harmless") default: print("Not a safe place for humans") } // print "Mostly weapons" / / associated valuesenum Barcode { case upc(Int, Int, Int, Int) case qrCode(String) } var productBarcode = Barcode.upc(8, 85909, 51226, 3) productBarcode = .qrCode("ABCDEFGHIJKLMNOP") switch productBarcode { case let .upc(numberSystem, manufacturer, product, check): print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).") case let .qrCode(productCode): print("QR code: \(productCode).") } // Print "QR Code: ABCDEFGHIJKLMNOP." // Recursive enumerationindirect enum ArithmeticExpression { case number(Int) case addition(ArithmeticExpression, ArithmeticExpression) case multiplication(ArithmeticExpression, ArithmeticExpression) } let five = ArithmeticExpression.number(5) let four = ArithmeticExpression.number(4) let sum = ArithmeticExpression.addition(five, four) let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2)) // 5 plus 4 times 2 func evaluate(_ expression: ArithmeticExpression) -> Int { switch expression { case let .number(value): return value case let .addition(left, right): return evaluate(left) + evaluate(right) case let .multiplication(left, right): return evaluate(left) * evaluate(right) } } print(evaluate(product)) / / print "18"Copy the code
Structs and classes
Structs and enumerations are value types, and classes are reference types
As a kind of general and flexible structure, structure and class have become the basis for people to build code. You can define attributes and add methods to your structures and classes using the same syntax that defines constants, variables, and functions. Example:
// Classes and structsstruct SomeStructure {
// Define the structure here}
class SomeClass {
// Define the class here} struct Resolution { var width = 0 var height = 0 } class VideoMode { var resolution = Resolution() var interlaced = false Var frameRate = 0.0 var name: String? } Copy the code
attribute
Store properties, evaluate properties, property watchers, property wrappers, global and local variables, type properties (static)
Property associates a value with a particular class, structure, or enumeration. Storing properties stores constants and variables as part of an instance, while computing properties directly compute (rather than store) values. Computed attributes can be used for classes, structures, and enumerations, while stored attributes can only be used for classes and structures. The property viewer monitors and responds to changes in property values. It is called every time a property is set, even if the new value is the same as the current value.
- WillSet is called before the new value is set
- DidSet is called after the new value has been set
The property wrapper adds a layer of separation between the code that manages how properties are stored and defined. Type properties are also accessed through the dot operator. However, type attributes are accessed through the type itself, not through the instance. Example:
/ / propertystruct Point {
Var x = 0.0, y = 0.0}
struct Size {
Var width = 0.0, height = 0.0} struct Rect { var origin = Point() Var size = size () // Store attributesVar center: Point {// Compute attributes get { let centerX = origin.x + (size.width / 2) let centerY = origin.y + (size.height / 2) return Point(x: centerX, y: centerY) } set(newCenter) { origin.x = newCenter.x - (size.width / 2) origin.y = newCenter.y - (size.height / 2) } } } Var square = Rect(origin: Point(x: 0.0, y: 0.0),Size: size (width: 10.0, height: 10.0))let initialSquareCenter = square.center Square. Center = Point(x: 15.0, y: 15.0)print("square.origin is now at (\(square.origin.x), \(square.origin.y))") Square. Origin is now at (10.0, 10.0) // Attribute wrapper@propertyWrapper struct TwelveOrLess { private var number = 0 var wrappedValue: Int { get { return number } set { number = min(newValue, 12) } } } Copy the code
methods
Instance Methods, type Methods (static)
Methods are functions associated with some particular type. Classes, structs, and enumerations can define instance methods; Instance methods encapsulate specific tasks and functions for instances of a given type. Classes, structs, and enumerations can also define type methods. Type methods are associated with the type itself. Example:
/ / methodclass Counter {
var count = 0
func increment() {
count += 1
} func increment(by amount: Int) { count += amount } func reset() { count = 0 } } Copy the code
The subscript
Subscripts, which can be defined in classes, structures, and enumerations, are shortcuts to access elements in collections, lists, or sequences
Subscript syntax, subscript usage, subscript options, type subscript (static)
subscript(index: Int) -> Int {
get {
// Return an appropriate Int value }
set(newValue) {
// Perform the appropriate assignment operation } } / / samplestruct TimesTable { let multiplier: Int subscript(index: Int) -> Int { return multiplier * index } } let threeTimesTable = TimesTable(multiplier: 3) print("six times three is \(threeTimesTable[6])") / / print "sixtimesThree is 18" var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4)numberOfLegs["bird"] = 2 // Type subscriptenum Planet: Int { case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune static subscript(n: Int) -> Planet { return Planet(rawValue: n)! } } let mars = Planet[4] print(mars) Copy the code
inheritance
Define a base class, subclass generation, override, prevent override (final)
Classes that do not inherit from other classes are called base classes. Example:
/ / inheritanceclass SomeClass: SomeSuperclass {
// Here is the definition of the subclass}
class Vehicle { Var currentSpeed = 0.0 var description: String { return "traveling at \(currentSpeed) miles per hour" } func makeNoise() { // Do nothing -- because the traffic is not necessarily noisy } } class Car: Vehicle { var gear = 1 override var description: String { return super.description + " in gear \(gear)" } } class AutomaticCar: Car { override var currentSpeed: Double { didSet { Gear = Int(currentSpeed / 10.0) + 1 } } } Copy the code
Construction process
The construction process is the preparation process before using an instance of a class, structure, or enumeration type.
Initial assignment to store attributes, custom constructor procedure, default constructor, constructor proxy for value types, class inheritance and construction procedure, fail constructor, required constructor
Constructors can perform part of the construction of an instance by calling other constructors. This process is called constructor proxy, and it prevents code duplication across multiple constructors. Swift provides two types of constructors for class types to ensure that all stored attributes in an instance get an initial value. These are called the specified constructor and the convenience constructor. You can add one or more failed constructors to the definition of a class, structure, or enumeration type. The syntax is to add a question mark (init?) after the init keyword. . The required constructor. Adding the required modifier before the constructor of a class indicates that all subclasses of that class must implement the constructor. Example:
// Construction processinit() {
// This is where the construction process is performed}
struct Fahrenheit { var temperature: Double init() { Temperature = 32.0 } } var f = Fahrenheit() print("The default temperature is \(f.temperature)° Fahrenheit") // Print The default temperature is 32.0° Fahrenheit struct Color { let red, green, blue: Double init(red: Double, green: Double, blue: Double) { self.red = red self.green = green self.blue = blue } init(white: Double) { red = white green = white blue = white } } Copy the code
The destructor process
Destructors are only applicable to class types and are called immediately before an instance of a class is released. Destructors are marked with the keyword deinit, just as constructors are marked with init. Swift automatically releases instances that are no longer needed to free up resources. Example:
// The destructor processdeinit {
// Execute the destructor}
class Bank { static var coinsInBank = 10_000 static func distribute(coins numberOfCoinsRequested: Int) -> Int { let numberOfCoinsToVend = min(numberOfCoinsRequested, coinsInBank) coinsInBank -= numberOfCoinsToVend return numberOfCoinsToVend } static func receive(coins: Int) { coinsInBank += coins } } class Player { var coinsInPurse: Int init(coins: Int) { coinsInPurse = Bank.distribute(coins: coins) } func win(coins: Int) { coinsInPurse += Bank.distribute(coins: coins) } deinit { Bank.receive(coins: coinsInPurse) } } Copy the code
Optional chained calls
An optional chained call is a method that can request and call properties, methods, and subscripts on an optional value whose current value may be nil. By placing a question mark (?) after the optional value of the attribute, method, or subscript that you want to call. Can define an optional chain. Similar to placing an exclamation point (!) after an optional value To force its value to expand. The main difference is that optional chained calls only fail when the optional value is null, whereas forced unrolling triggers a runtime error. Example:
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1 } let john = Person() letroomCount = john.residence! .numberOfRooms// This raises a runtime error if letroomCount = john.residence? .numberOfRooms { print("John's residence has \(roomCount) room(s).") } else { print("Unable to retrieve the number of rooms.") } Unable to retrieve the number of rooms. john.residence = Residence() if letroomCount = john.residence? .numberOfRooms { print("John's residence has \(roomCount) room(s).") } else { print("Unable to retrieve the number of rooms.") } / / print "JohnThe residence has 1 room(s)."Copy the code
Error handling
Error handling is the process of responding to and recovering from errors. Swift provides first-class support at runtime for throwing, catching, passing, and manipulating recoverable errors.
Represents and throws errors, handles errors, and specifies cleanup operations
In Swift, errors are represented by values of the type following the Error protocol. There are four ways to handle errors in Swift. You can pass an error thrown by a function to the code calling it (throws), handle the error with a do-catch statement, or treat the error as an optional type (try?). , or assert that the error does not occur at all (try!) . The defer statement defer execution of the code until the current scope exits. Example:
// Error handlingenum VendingMachineError: Error {
caseInvalidSelection invalid caseThe sufficientFunds(coinsNeeded: Int) were insufficient caseOutOfStock / / out of stock} throw VendingMachineError.insufficientFunds(coinsNeeded: 5) var vendingMachine = VendingMachine() vendingMachine.coinsDeposited = 8 do { try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine) print("Success! Yum.") } catch VendingMachineError.invalidSelection { print("Invalid Selection.") } catch VendingMachineError.outOfStock { print("Out of Stock.") } catch VendingMachineError.insufficientFunds(let coinsNeeded) { print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.") } catch { print("Unexpected error: \(error).") } Insufficient funds. Please insert an additional 2 coins. // Specify the cleanup operationfunc processFile(filename: String) throws { if exists(filename) { let file = open(filename) defer { close(file) } while let line = try file.readline() { // Processing files. } // Close (file) is called here, at the end of the scope. } } Copy the code
Type conversion
Type conversions are implemented in Swift using the IS and AS operators. The two operators provide a simple way to check the type of a value or convert it, respectively.
Define class hierarchies for type conversions, check for types (is), transition down (as? Or as! , Any and AnyObject
Casts can be used in class and subclass hierarchies to check the type of a particular class instance and cast the type of that class instance to another type in the hierarchy. Swift provides two special type aliases for indeterminate types:
- Any can represent Any type, including function types.
- AnyObject can represent an instance of any class type.
Example:
// Type conversion// A base class MediaItemclass MediaItem {
var name: String
init(name: String) {
self.name = name } } class Movie: MediaItem { var director: String init(name: String, director: String) { self.director = director super.init(name: name) } } class Song: MediaItem { var artist: String init(name: String, artist: String) { self.srtist = artist super.init(name: name) } } let library = [ Movie(name: "Casablanca", director: "Micheal Curtiz"), Song(name: "Blue Suede Shose", artist: "Elvis Presley"), Movie(name: "Citizen Kane", director: "Orson Wells"), Song(name: "The One And Only", artist: "Chesney Hawkes"), Song(name: "Never Gonna Give You Up", artist: "Rick Astley") ] var movieCount = 0 var songCount = 0 for item in library { if item is Movie { movieCount += 1 } else if item is Song { songCount += 1 } } print("Media library contains \(movieCount) movies and \(songCount)") // Print "Media Library contains 2 movies and 3 Songs" for item in library { if let movie = item as? Movie { print("Movie: \(movie.name), dir. \(movie.director)") } else if let song = item as? Song { print("Song: \(song.name), by \(song.artist)") } } // Movie: Casablanca, dir. Michael Curtiz // Song: Blue Suede Shoes, by Elvis Presley // Movie: Citizen Kane, dir. Orson Welles // Song: The One And Only, by Chesney Hawkes // Song: Never Gonna Give You Up, by Rick Astley Copy the code
Nested types
Swift allows you to define nested types, nested enumerations, classes, and constructs within supported types.
The nested type practice refers to nested types
To nest a type within another type, write the definition of the nested type within {} of its outer type, and you can define multiple levels of nesting as needed. Example:
// Nested typesstuct BlackjackCard {
// Nested Suit enumeration enum Suit: Character {
case spades = "1", hearts = "2", diamonds = "3", clubs = "4"
} // Nested Rank enumeration enum Rank: Int { case two = 2, three, four, five, six, seven, eight, nine, ten case jack, queen, king, ace struct Values { let first: Int, second: Int? } var values: Values { switch self { case .ace: return Values(first: 1, second: 11) case .jack, .queen, .king: return Values(first: 10, second: nil) default: return Values(first: self.rawValue, second: nil) } } } // BlackjackCard properties and methods let rank: Rank, suit: Suit var description: String { var output = "suit is \(suit.rawValue)," output += " value is \(rank.values.first)" if let second = rank.values.second { output += " or \(second)" } return output } } let theAceOfSpades = BlackjackCard(rank: .ace, suit: .spades) print("theAceOfSpades: \(theAceOfSpades.description)") // Print theAceOfSpades: suit is 1, value is 1 or 11 let heartsSymbol = BlackjackCard.Suit.hearts.rawValue / / 2Copy the code
extension
Extensions can add functionality to an existing class, structure, enumeration, or protocol.
Extended syntax, computed attributes, constructors, methods, subscripts, nested types
Extensions in Swift can:
- Add computational instance attributes and computational class attributes
- Define instance methods and class methods
- Provide a new constructor
- Define the subscript
- Define and use new nested types
- Conform an existing type to an agreement
Extended syntax:
extension SomeType {
// Add new functionality to SomeType here}
Copy the code
Extensions can add computed instance attributes and computed class attributes to existing types. Extensions can add new constructors to existing types. Extensions can add new instance and class methods to existing types. Extensions can add new subscripts to existing types. Extensions can add new nested types to existing classes, structures, and enumerations. Example:
// Extended syntaxextension SomeType {
// Add new functionality to SomeType here}
// Add one or more protocolsextension SomeType: SomeProtocol, AnotherProtocol { // The required implementation of the protocol is written here} struct Size { Var width = 0.0, height = 0.0} struct Point { Var x = 0.0, y = 0.0} struct Rect { var origin = Point() var size = Size() } extension Rect { init(center: Point, size: Size) { let originX = center.x - (size.width / 2) let originY = center.y - (size.height / 3) self.init(origin: Point(x: originX, y: originY), size: size) } } letCenterRect = Rect(center: Point(x: 4.0, y: 4.0),Size: size (width: 3.0, height: 3.0))CenterRect origin is (2.5, 2.5) and its size is (3.0, 3.0) extension Int { func repetitions(task: () -> Void) { for _ in0.. <self { task() } } } 3.repetitions { print("Hello!") } // Hello! // Hello! // Hello! extension Int { mutating func square() { self = self * self } } var somtInt = 3 someInt.square() // someInt is now 9Copy the code
agreement
A protocol defines a blueprint that specifies the methods, properties, and other things needed to implement a particular task or function. Classes, structs, or enumerations can all follow the protocol and provide concrete implementations of the requirements defined by the protocol.
Protocol syntax, attribute requirements, method requirements, variant method requirements, Constructor requirements, protocol as a type, delegation, collection of protocol types, Protocol inheritance, class-specific protocols, protocol composition, checking protocol conformance, Optional protocol requirements, Protocol extension,
Protocol syntax
protocol SomeProtocol {
// This is the definition part of the protocol}
Copy the code
Protocols can require protocol-following types to provide specific names and instance or type attributes of the type. A protocol may require that the type that follows the protocol implement certain specified instance or class methods. In instance methods of value types (that is, structs and enumerations), prefix the method with the mutating keyword before the func keyword to indicate that the instance to which it belongs and the value of any attribute of that instance can be modified in the method. A protocol can require that a specified constructor be implemented that follows the type of the protocol. Delegation is a design pattern that allows a class or structure to delegate functionality for which they are responsible to an instance of another type. Example:
// Protocol syntaxprotocol SomeProtocol {
// This is the definition part of the protocol}
struct SomeStructure: FirstProtocol, AnotherProtocol { // This is the definition part of the structure} class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol { // This is the definition part of the class} protocol SomeProtocol { var mustBeSettable: Int { get set } var doesNotNeedToBeSettable: Int { get } } protocol AnotherProtocol { static var someTypeProperty: Int { get set } } protocol FullyNamed { var fullName: String { get } } struct person: FullyNamed { var fullName: String } let john = Person(fullName: "John Appleseed") / / John fullName"John Appleseed" class Starship: FullyNamed { var prefix: String? var name: String init(name: String, prefix: String? = nil) { self.name = name self.prefix = prefix } var fullName: String { return(prefix ! = nil ? prefix! +"" : "") + name } } var ncc1701 = Starship(name: "Enterprise", prefix: "USS") / / ncc1701 fullName"USS Enterprise" Copy the code
The generic
Generic code allows you to write flexible, reusable functions and types that work for any type, depending on your own requirements. You can avoid writing duplicate code and instead express the intent of your code in a clear and abstract way.
Generic functions, type parameters, named type parameters, generic types, generic extensions, type constraints, associated types
Example:
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
func swapTwoValues<T>(_ a: inout T, _ b: inout T) { let temporaryA = a a = b b = temporaryA } func swapTwoInts(_ a: inout Int, _ b: inout Int) func swapTwoValues<T>(_ a: inout T, _ b: inout T) var someInt = 3 var anotherInt = 107 swapTwoValues(&someInt, &anotherInt) // someInt is now 107, anotherInt is now 3 var someString = "hello" var anotherString = "world" swapTwoValues(&someString, &anotherString) // someString is now "world", anotherString is now "hello"Copy the code
Opaque type
A function or method with an opaque return type hides the type information of the return value. Instead of providing a specific type as a return type, the function describes the return value in terms of the protocol it supports.
Opaque type solves the problem, returns the opaque type, the difference between the opaque type and the protocol type
Hiding the type information is useful when dealing with the relationship between the module and the calling code, because the underlying data type returned can still remain private. Opaque types are the opposite of generic types. Opaque types allow a function implementation to select a return type independent of the calling code. If an opaque type is returned in more than one place in a function, all possible return values must be of the same type. The main difference between the return opaque type and the return protocol type is the need to ensure type consistency. An opaque type can only correspond to one specific type, even if the function caller doesn’t know which type it is. A protocol type can correspond to multiple types at the same time, as long as they all follow the same protocol. Example:
protocol Shape {
func draw() -> String
}
struct Triangle: Shape {
var size: Int func draw() -> String { var result = [String]() for length in1... size { result.append(String(repeating: "*", count: length)) } return result.joined(separator: "\n") } } let smallTriangle = Triangle(size: 3) print(smallTriangle.draw()) / / */ / * */ / * * * struct FlippedShape<T: Shape>: Shape { var shape: T func draw() -> String { let lines = shape.draw().split(separator: "\n") return lines.reversed().joined(separator: "\n") } } let flippedTriangle = FlippedShape(shape: smallTriangle) print(flippedTriangle.draw()) / / * * */ / * */ / *Copy the code
Automatic reference counting
Swift uses an automatic reference counting (ARC) mechanism to track and manage your application’s memory. This is the case if two class instances hold strong references to each other, so that each instance keeps the other alive. This is known as a circular strong reference. Swift provides two ways to solve the circular strong reference problem you encounter when using class attributes: weak references and unmaster references (unowned reference).
- When declaring a property or variable, precede it with the weak keyword to indicate that it is a weak reference.
- When declaring a property or variable, precede it with the keyword unowned to indicate that it is an unmanned reference.
Example:
// Automatic reference counting practicesclass Person {
let name: String
init(name: String) {
self.name = name
print("\(name) is being initialized") } deinit { print("\(name) is being deinitialized") } } var reference1: Person? var reference2: Person? var reference3: Person? reference1 = Person(name: "John Appleseed") // Print "John Appleseed is being initialized"reference2 = reference1 reference3 = reference1 reference1 = nil reference2 = nil reference3 = nil // Print "John Appleseed is being deinitialized" // Circular strong referencesclass Person { let name: String init(name: String) { self.name = name } var apartment: Apartment? deinit { print("\(name) is being deinitialized")}} class Apartment { let unit: String init(unit: String) { self.unit = unit } var tenant: Person? deinit { print("Apartment \(unit) is being deinitialized")}} var john: Person? var unit4A: Apartment? john = Person(name: "John Appleseed") unit4A = Apartment(unit: "4A") john = nil unit4A = nil / / weak referencesclass Person { let name: String init(name: String) { self.name = name } var apartment: Apartment? deinit { print("\(name) is being deinitialized")}} class Apartment { let unit: String init(unit: String) { self.unit = unit } weak var tenant: Person? deinit { print("Apartment \(unit) is being deinitialized")}} var john: Person? var unit4A: Apartment? john = Person(name: "John Appleseed") unit4A = Apartment(unit: "4A") john! .apartment = unit4Aunit4A! .tenant = john john = nil // Print "John Appleseed is being deinitialized"Copy the code
Memory safety
By default, Swift prevents unsafe behavior in your code.
Understand memory access conflicts, in-out parameter access conflicts, method access conflicts self access conflicts, property access conflicts
Example:
func balance(_ x: inout Int, _ y: inout Int) {
let sum = x + y
x = sum / 2
y = sum - x
}
var playerOneScore = 42 var playerTwoScore = 30 Balance (&playerOneScore, &playerTwoScore) // Normalbalance(&playerOneScore, &playerOneScore) // Error: playerOneScore access conflictCopy the code
Access control
Access control can restrict access to your code by other source files or modules.
- The open and public levels allow entities to be accessed by all entities in the source file of the same module, or by importing the module from outside the module. Typically, you use the Open or public level to specify the external interface to the framework.
- The internal level allows entities to be accessed by any entity in the same module source file, but not by entities outside the module. In general, an interface can be set to internal if it is only used within the application or framework.
- Fileprivate restricts entities to access only within the files they define. Fileprivate can be used to hide partial implementation details of a feature if they only need to be used within a file.
- Private restricts access to entities only in the scope they define and to extensions within the same file. You can use private to hide some of the details of a function if they only need to be used within the current scope.
Open is the highest access level (the least restricted), and private is the lowest access level (the most restricted). Open applies only to classes and members of classes. It differs from public in that open qualifies classes and members that can be inherited and overridden outside of a module. Example:
public class SomePublicClass {}
internal class SomeInternalClass {}
fileprivate class SomeFilePrivateClass {}
private class SomePrivateClass {}
Class SomeInternalClass {Var someInternalConstant = 0 // Public class SomePublicClass {// Explicit public classPublic var somePublicProperty = 0 // Explicit public class memberVar someInternalProperty = 0 // Implicit internal class member fileprivate func someFilePrivateMethod() {} // An explicit fileprivate member private func somePrivateMethod() {} // An explicit private class member} Class SomeInternalClass {// Implicit internal classVar someInternalProperty = 0 // Implicit internal class member fileprivate func someFilePrivateMethod() {} // An explicit fileprivate member private func somePrivateMethod() {} // An explicit private class member} Fileprivate class SomeFilePrivateClass {// Explicit fileprivate class func someFilePrivateMethod() {} // An implicit Fileprivate member private func somePrivateMethod() {} // An explicit private class member} Private class SomePrivateClass {// private class SomePrivateClass func somePrivateMethod() {} // Implicit private class members} Copy the code
Advanced operator
Swift also provides several advanced operators that can perform complex operations on numeric values. They contain bitwise operators and shift operators.
Bitwise operators, overflow operators, precedence and associativity, operator functions, custom operators
Example:
let initialBits: UInt8 = 0b00001111
letInvertedBits = ~initialBits // = 0b11110000
var potentialOverflow = Int16.max
// The value for potentialOverflow is 32767, which is the maximum integer that Int16 can holdpotentialOverflow += 1 // An error is reported struct Vector2D { Var x = 0.0, y = 0.0} extension Vector2D { static func + (left: Vector2D, right: Vector2D) -> Vector2D { return Vector2D(x: left.x + right.x, y: left.y + right.y) } } letVector = Vector2D(x: 3.0, y: 1.0)letOthervector = Vector2D(x: 2.0, y: 4.0)let combinedVector = vector + anotherVector // The combinedVector is a new Vector2D instance with values (5.0, 5.0).Copy the code
The public,
Wukong chat architecture attention to receive 111 architect data