Swift Advanced Gold Road (2)

This is the answer to the question of whether the article is the king of Swift or bronze. These problems are only the surface concept, belong to the knowledge point, in my opinion, even if it is very clear and can not represent the king, if you have to use the analogy, gold or reasonable πŸ˜„.

Swift is an easy but difficult language to master. Even if none of the following is clear, it doesn’t stop you from developing your business requirements, but knowing it will help you write Swifty code.

1. Protocol

ExpressibleByDictionaryLiteral

ExpressibleByDictionaryLiteral is a dictionary of literal agreement, the agreement of the complete written as follows:

public protocol ExpressibleByDictionaryLiteral {
    /// The key type of a dictionary literal.
    associatedtype Key
    /// The value type of a dictionary literal.
    associatedtype Value
    /// Creates an instance initialized with the given key-value pairs.
    init(dictionaryLiteral elements: (Self.Key.Self.Value)... }Copy the code

First, Literal means a notation used to express a fixed value in source code.

For example, building a dictionary can be done in two ways:

// Method 1:
var countryCodes = Dictionary<String.Any>()
countryCodes["BR"] = "Brazil"
countryCodes["GH"] = "Ghana"
// Method 2:
let countryCodes = ["BR": "Brazil"."GH": "Ghana"]
Copy the code

The second way to construct is by means of literals.

Base types are constructed from literals:

let num: Int = 10
let flag: Bool = true
let str: String = "Brazil"
let array: [String] = ["Brazil"."Ghana"]
Copy the code

And these all have their literal protocols:

ExpressibleByNilLiteral // nil literal protocol
ExpressibleByIntegerLiteral // Integer literal protocol
ExpressibleByFloatLiteral // Floating-point numeric surface protocol
ExpressibleByBooleanLiteral // Boolean literal protocol
ExpressibleByStringLiteral // Literal string protocol
ExpressibleByArrayLiteral // Array literal protocol
Copy the code

Sequence

Sequence translates to Sequence. The purpose of this protocol is to be a set of values of the same type, and to provide the ability to iterate over these values. Iteration here can be understood as traversal, or for-in. See the definition of this agreement:

protocol Sequence {
    associatedtype Iterator: IteratorProtocol
    func makeIterator(a) -> Iterator
}
Copy the code

Sequence introduces another protocol, the IteratorProtocol, to provide Sequence iteration capability.

public protocol IteratorProtocol {
    associatedtype Element
    public mutating func next(a) -> Self.Element?
}
Copy the code

We usually use for-in to iterate over arrays:

let animals = ["Antelope"."Butterfly"."Camel"."Dolphin"]
for animal in animals {
    print(animal)
}
Copy the code

Here the for-in is translated by the compiler as:

var animalIterator = animals.makeIterator()
while let animal = animalIterator.next() {
    print(animal)
}
Copy the code

Collection

Collection is translated as a Collection, which inherits from Sequence.

public protocol Collection : Sequence {
  associatedtype Index : Comparable
  var startIndex: Index { get }
  var endIndex: Index { get }
  var isEmpty: Bool { get }
  var count: Int { get }
  
  subscript(position: Index) - >Element { get }
  subscript(bounds: Range<Index- > >)SubSequence { get}}Copy the code

Is a finite Collection whose elements can be iterated over and accessed through the index’s subscript. Note that the Sequence may be infinite and the Collection must be finite.

On the basis of Sequence, Collection extends the properties of subscript access and element number. Our common collection types Array, Dictionary, and Set all follow this protocol.

CustomStringConvertible

This protocol represents the style of the output of a custom type. Let’s start with the definition:

public protocol CustomStringConvertible {
    var description: String { get}}Copy the code

There’s only one property called description. It’s simple to use:

struct Point: CustomStringConvertible {
    let x: Int, y: Int
    var description: String {
        return "(\(x).\(y))"}}let p = Point(x: 21, y: 30)
print(p) / / (21, 30)
//String(describing: <#T##CustomStringConvertible#>)
let s = String(describing: p)
print(s) / / (21, 30)
Copy the code

If you do not implement CustomStringConvertible and print the object directly, the system will output with the default Settings. We can use CustomStringConvertible to set the output behavior, and a deal is CustomDebugStringConvertible:

public protocol CustomDebugStringConvertible {
    var debugDescription: String { get}}Copy the code

As with CustomStringConvertible, corresponds to the output of debugPrint.

Hashable

We commonly used Dictionary, Set are implemented Hashable protocol. The purpose of the Hash is to reduce the time complexity of finding an element of the set to O(1). To achieve this, a one-to-one correspondence between the elements of the set and the stored address should be suggested.

Let’s look at the definition of Hashable’s protocol:

public protocol Hashable : Equatable {
    var hashValue: Int { get }
    func hash(into hasher: inout Hasher)
}

public protocol Equatable {
    static func= =(lhs: Self, rhs: Self) -> Bool
}
Copy the code

Noting func hash(into Hasher: Inout hasher), Swift 4.2 further optimizes Hashable by introducing the hasher type and adopting a new generic hash function.

If you want to customize how your type implements Hashable, you can override the hash(into:) method instead of hashValue. Hash (into:) adds the necessary state information for the type by passing a Hasher reference object and then calling combine(_:) from this object.

/ / Swift > = 4.2
struct Color: Hashable {
    let red: UInt8
    let green: UInt8
    let blue: UInt8

    // Synthesized by compiler
    func hash(into hasher: inout Hasher) {
        hasher.combine(self.red)
        hasher.combine(self.green)
        hasher.combine(self.blue)
    }

    // Default implementation from protocol extension
    var hashValue: Int {
        var hasher = Hasher(a)self.hash(into: &hasher)
        return hasher.finalize()
    }
}
Copy the code

Reference: Hashable/Hasher

Codable

Codable is another name for the Decodable and Encodable types. It can serialize data structures within a program into exchangeable data, and deserialize common data formats into data structures for internal use, greatly improving the experience of converting objects and their representations to and from one another. The problem is that we often encounter JSON to model, and model to JSON.

public typealias Codable = Decodable & Encodable

public protocol Decodable {
    init(from decoder: Decoder) throws
}
public protocol Encodable {
    func encode(to encoder: Encoder) throws
}
Copy the code

Here is just a simple decoding process:

/ / the json data
{
    "id": "1283984"."name": "Mike"."age": 18
}
// Define the object
struct Person: Codable{
    var id: String
    var name: String
    var age: Int
}
// Json is the Data type returned by the network interface
let mike = try! JSONDecoder().decode(Person.self, from: json)
print(mike)
Student(id: "1283984", name: "Mike", age: 18)
Copy the code

Codable is easy to implement and supports custom decomencodings for portable applications. It can replace SwiftyJSON, HandyJSON, and other codec libraries.

Comparable

This is the protocol used to implement the comparison function. It is defined as follows:

public protocol Comparable : Equatable {
  
    static func < (lhs: Self, rhs: Self) -> Bool

    static func <= (lhs: Self, rhs: Self) -> Bool

    static func> =(lhs: Self, rhs: Self) -> Bool

    static func > (lhs: Self, rhs: Self) -> Bool
}
Copy the code

It inherits from Equatable, the agreement to judge equality. You can clearly understand the definition of the implementation of a variety of comparison with the function of comparison. This is not a comparison.

RangeReplaceableCollection

RangeReplaceableCollection support elements are replaced with another set of elements of anyon range of collection.

Look at the definition:

public protocol RangeReplaceableCollection : Collection where Self.SubSequence : RangeReplaceableCollection {

    associatedtype SubSequence
  
    mutating func append(_ newElement: Self.Element)
    mutating func insert<S>(contentsOf newElements: S, at i: Self.Index) where S : Collection.Self.Element= =S.Element
    /* Concatenation, insert, delete, replace methods, all of which have the ability to operate on group elements */
  
    override subscript(bounds: Self.Index) - >Self.Element { get }
    override subscript(bounds: Range<Self.Index- > >)Self.SubSequence { get}}Copy the code

For example, if an Array supports this protocol, we can do the following:

var bugs = ["Aphid"."Damselfly"]
bugs.append("Earwig")
bugs.insert(contentsOf: ["Bumblebee"."Cicada"], at: 1)
print(bugs)
// Prints "["Aphid", "Bumblebee", "Cicada", "Damselfly", "Earwig"]"
Copy the code

Here is a diagram of the protocols followed by arrays in Swift to help you understand the relationships among the protocols described above:

Image source: swiftdoc.org/v3.1/type/a…

Second, @ propertyWrapper

Read the following code. What does print print

@propertyWrapper
struct Wrapper<T> {
    var wrappedValue: T

    var projectedValue: Wrapper<T> { return self }

    func foo(a) { print("Foo")}}struct HasWrapper {@Wrapper var x = 0
    
    func foo(a) {
        print(x) / / 0
        print(_x) // Wrapper<Int>(wrappedValue: 0)
        print($x) // Wrapper<Int>(wrappedValue: 0)}}Copy the code

This code seems to be looking for an understanding of @propertyWrapper, but it has a lot of garbage, which makes the code weird.

@propertywrapper stands for propertyWrapper, which can unify a set of similar property methods. For example, if we want to add a value in UserDefaults for whether it is first started, we can normally handle this:

extension UserDefaults {
    enum Keys {
      static let isFirstLaunch = "isFirstLaunch"
    }
    var isFirstLaunch: Bool {
        get {
            return bool(forKey: Keys.isFirstLaunch)
        }
        set {
            set(newValue, forKey: Keys.isFirstLaunch)
        }
    }
}
Copy the code

If we need to add a lot of these properties, we need to write a lot of get and set methods. The @propertyWrapper function is to provide a template for this setting of properties. Here’s how to write it using property wrappers.

@propertyWrapper
struct UserDefaultWrapper<T> {
    private let key: String
    private let defaultValue: T
    init(key: String, defaultValue: T) {
        self.key = key
        self.defaultValue = defaultValue
    }
  
    var wrappedValue: T {
        get {
            UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
        }
        set {
            UserDefaults.standard.set(newValue, forKey: key)
        }
    }
}

class UserDefaults {@UserDefaultWrapper(key: Keys.isFirstLaunch, defaultValue: false)
    var isFirstLaunch: Bool
}

Copy the code

An @propertyWrapper constrained object must define the wrappedValue property because the properties wrapped by the object will go to the implementation of wrappedValue.

Going back to the instance code, it is permissible to define wrappedValue without adding any implementation. So when you access x you are actually accessing wrappedValue of the Wrapper, which prints out 0 because there is no implementation given. _x and $x correspond to the Wrapper itself.

Reference: Swift Property Wrappers

Three, keywords

public open

Public open is the permission keyword. For a rigorous project, precisely minimizing the level of access control is important for code maintenance. The complete permission keywords, in order of permission size, are as follows:

open > public > internal > fileprivate > private

  • openMaximum permission to allow external module access, inheritance, and override.
  • publicAllow external module access, but not inheritance, override.
  • internalIs the default keyword and can be shared within the same module.
  • fileprivateIndicates that code can be accessed in the current file without type qualification.
  • privateIndicates that code can only be used in the current scope or in the same type of scope in the same file.

These permission keywords can be decorated with attributes, methods, and types. Note: When a property of a type is to be decorated with public, the type must be fixed with at least the public (or open) permission keyword. In order to obtain a certain attribute or method, we need to obtain the type first, so the access permission of the outer layer (type) must be greater than or equal to the access permission of the inner layer (type, method, attribute).

Reference: Swift AccessControl

static class final

In the original text, “final” is put together with the permission keyword. In fact, it is unreasonable to put it here to discuss.

Static variable keywords, derived from C language.

The following scenarios are commonly used in Swift:

// Used only before the class name to indicate that the class cannot be inherited. Only class types are supported
final class Manager {
    // Declare singletons
    static let shared = Manager(a)// Instance properties, which can be overridden
    var name: String = "Ferry"
    // Instance properties, which cannot be overridden
    final var lastName: String = "Zhang"
    // Class attributes that cannot be overridden
    static var address: String = "Beijing"
    // Class attributes, which can be overridden. Note that it is only a computational property, not a storage property
    class var code: String {
        return "0122"
    }
  
    // Instance functions can be overridden
    func download(a) {
      /* code... * /
    }
    // The instance function cannot be overridden
    final func download(a) {
      /* code... * /
    }
    // Class functions that can be overridden
    class func removeCache(a){
     	/* code... * / 
    }
    // Class function, cannot be overridden
    static func download(a) {
      /* code... * /}}struct Manager {
    // Declare singletons
    static let shared = Manager(a)/ / class attribute
    static var name: String = "Ferry"
    / / function
    static func download(a) {
      /* code... * /}}Copy the code

Since structs and enums cannot be inherited, they cannot use the class and final keywords. They can only be qualified by the static keyword

mutating inout

Mutating is mostly used to modify struct objects until it modifies functions that change that type. Here’s an example:

struct Point {
    var x: CGFloat
    var y: CGFloat
    // Since this method changes the struct attribute value (x), mutating must be added
    mutating func moveRight(offset: CGFloat) {
        x += offset
    }

    func normalSwap(a: CGFloat, b: CGFloat) {
        let temp = a
        a = b
        b = temp
    }
    // To exchange two values, pass in the address of the object. Note that inout needs to be loaded before the type name
    func inoutSwap(a: inout CGFloat, b: inout CGFloat) {
        let temp = a
        a = b
        b = temp
    }
}

var location1: CGFloat = 10
var location2: CGFloat = -10

var point = Point.init(x: 0, y: 0)
point.moveRight(offset: location1)
print(point)	/ / Point (0.0) 10.0 x: y:

point.normalSwap(a: location1, b: location2)
print(location1)	/ / 10
print(location2)	/ / - 10
// Note that the prefix & is required
point.inoutSwap(a: &location1, b: &location2)
print(location1)	/ / - 10
print(location2)	/ / 10
Copy the code

Inout requires a value character to be passed in, so changing it causes the object to change. Take a look back at one of Hashable’s protocol implementations:

func hash(into hasher: inout Hasher) {
    hasher.combine(self.red)
    hasher.combine(self.green)
    hasher.combine(self.blue)
}
Copy the code

Only with inout can you change the value of the hasher that is passed in.

infix operator

The infix operator is the infix operator, as well as the prefix and postfix operators.

It is used to define custom operators. For example, in Python, we can use ** to exponentiate, but in Swift, we can use custom operators to define a power implemented with **.

// Define the infix operator
infix operatorζ”―ι‚£// Implement the logic of the operator. Two parameters are required for the infix
funcζ”―ι‚£(left: Double, right: Double) -> Double {
    return pow(left.right)}let number = 2ζ”―ι‚£3
print(value) / / 8
Copy the code

In the same way we can define the prefix and postfix operators:

// Define the factorial operator, the suffix operator
postfix operator ~!
postfix func ~! (value: Int) -> Int {

    func factorial(_ value: Int) -> Int {
        if value <= 1 {
            return 1
        }
        return value * factorial(value - 1)}return factorial(value)
}
// Define the output operation, prefix operator
prefix operator <<
prefix func << (value: Any) {
    print(value)
}

let number1 = 4~!
print(number1) // 24

<<number1 // 24
<<"zhangferry" // zhangferry
Copy the code

Prefixes and suffixes require only one operand, so only one parameter is required.

More information about Operators can be found here: Swift Operators.

Note that since this article is older, some of the definitions of operators have changed.

@ dynamicMemberLookup, @ dynamicCallable

I really haven’t used these two keywords, but look at dynamic and you can see that these two features are designed to make Swift dynamic.

@dynamicMemberLookup is called dynamic lookup member. After using @dynamicMemberLookup to tag objects (object, structure, enumeration, protocol), we can access properties that do not exist in the object after implementing subscript(dynamicMember Member: String) method. If the accessed property does not exist, the subscript(dynamicMember member: String) method of the implementation is called, and the key is passed in as a member. Here’s an example:

@dynamicMemberLookup
struct Person {
    subscript(dynamicMember member: String) - >String {
        let properties = ["nickname": "Zhuo"."city": "Hangzhou"]
        return properties[member, default: "undefined"]}}// Execute the following code
let p = Person(a)print(p.city)	//Hangzhou
print(p.nickname)	//Zhuo
print(p.name)	//undefined
Copy the code

We have not defined the city, nickname, name properties of Person, but we can use dot syntax to try to access them. If @dynamicMemberLookup is not used, it will be checked by the compiler and an error will be reported, but if the keyword is added, the compiler will pass it regardless of whether it exists or not.

@dynamicCallable
struct Person {
    // Implement method 1
    func dynamicallyCall(withArguments: [String]) {
        for item in withArguments {
            print(item)
        }
    }
    // Implement method 2
    func dynamicallyCall(withKeywordArguments: KeyValuePairs<String, String>){
        for (key, value) in withKeywordArguments {
            print("\(key) --- \(value)")}}}let p = Person()
p("zhangsan")
// equals p.dynamicallyCall(withArguments: ["zhangsan"])
p("zhangsan"."20"."Male")
// equal p.dynamicallyCall(withArguments: ["zhangsan", "20", "male "])
p(name: "zhangsan")
// = p.dynamicallyCall(withkeyWordargCall: ["name": "zhangsan"])
p(name: "zhangsan", age:"20", sex: "Male")
// = p.dynamicallyCall(withkeyWordargcall: ["name": "zhangsan", "age": "20", "sex": "m "])
Copy the code

DynamicallyCall (withArguments:) or dynamicallyCall(withKeywordArguments:) can be implemented when making this declaration for a type. The compiler will allow you to call and define methods for.

With a dynamic lookup of member variables and a dynamic method call, Swift can become a fully dynamic language. Therefore, as a static language, Swift can also have dynamic characteristics.

Dynamic Member Lookup is a new feature in Swift 4.2

where

Where is usually used as a condition. It can be used in for-in, swith, do-catch:

let numbers = [1.2.3.4.5.6.7.8.9]
for item in numbers where item % 2= =1 {
    print("odd: \(item)")	// output 1,3,5,7,9, etc
}

numbers.forEach { (item) in
    switch item {
    case let x where x % 2= =0:
        print("even: \(x)") // output 2,4,6,8, etc
    default:
        break}}Copy the code

Where can also be used for type qualification.

We can extend the merge function of a dictionary to merge two dictionaries using the dictionary for the same Key value. And I only want to use this method for dictionaries where both Key and Value are strings.

// The Key Value is derived from the generic type defined in the Dictionary
extension Dictionary where Key= =String.Value= =String {
    // The same key operation overwrites the old value
    func merge(other: Dictionary) -> Dictionary {
        return self.merging(other) { _, new in new }
    }
}
Copy the code

@autoclosure

@autoclosure is used before the closure type. What it does is it automatically wraps an expression into a closure (closure).

For example, we have a method that accepts a closure and prints when the closure executes true, using the normal closure and the closure with autoclosure:

func logIfTrueNormal(predicate: (a) -> Bool) {
    if predicate() {
        print("True")}}// Note that @autoclosure is added to the front of closures
func logIfTrueAutoclosure(predicate: @autoclosure (a) -> Bool) {
    if predicate() {
        print("True")}}// Call mode
logIfTrueNormal(predicate: {3 > 1})
logIfTrueAutoclosure(predicate: 3 > 1)
Copy the code

The compiler converts the expression 3 > 1 in the logIfTrueAutoclosure argument to {3 > 1} trailing closure style.

So what’s the use of this notation? As an example, the @autoclosure tag is used in several short-circuit operators provided by the Swift system (that is, if the left side of the expression is determined, the right side will not be evaluated). Then?? For example, the implementation of the operator looks like this:

public func ?? <T>(optional: T? , defaultValue: @autoclosure(a) throws -> T)
    rethrows -> T {
  switch optional {
  case .some(let value):
    return value
  case .none:
    return try defaultValue()
  }
}
/ / use
var name: String? = "ferry"
let currentName = name ?? getDefaultName()
Copy the code

Because the @autoclosure tag is used, the closure is not valid. We can use an expression for the defaultValue argument of, and because it is a closure, when the name is non-null, the value is returned directly without calling getDefaultName(), reducing the computation.

Reference: @autoclosure and?? Note that the instance code is not running due to the Swift version.

@escaping

@escaping is also a closure modifier, the closure that you tag with it is called the escape closure, and the keyword @noescape, the closure that you tag with it, is called the non-escape closure. In versions of Swift3 and later, the default closure is the non-escape closure, and before that the default closure was the escape closure.

The difference between the two is mainly the difference in the declaration period. When the closure is taken as an argument, it is a non-escape closure if the declaration period is the same as the function, and an escape closure if the declaration period is greater than the function. Use examples to understand:

// Non-escape closure
func logIfTrueNormal(predicate: (a) -> Bool) {
    if predicate() {
        print("True")}}// Escape the closure
func logIfTrueEscaping(predicate: @escaping (a) -> Bool) {
    DispatchQueue.main.async {
        if predicate() {
            print("True")}}}Copy the code

The closure of the second function is an escape closure because it is an asynchronous call, and when the function exits, the closure is still present, with a longer declaration period than the function.

If you can’t tell whether you should use an escape or non-escape closure, don’t worry, because the compiler will do that for you. The second function, the compiler will report an error if we do not declare Escaping closures, warning us: Escaping closure captures non-escaping parameter ‘predicate’. Still, we have to understand the difference.

Higher order functions

Filter, Map, Reduce, flatmap, compactMap

These higher-order functions are used on array objects, so let’s look at them with an example:

let numbers = [1.2.3.4.5.6.7.8.9]
/ / filter to filter
let odd = numbers.filter { (number) -> Bool in
    return number % 2= =1
}
print(odd) // [1, 3, 5, 7, 9]

/ / map
let maps = odd.map { (number) -> String in
    return "\(number)"
}
print(maps) // ["1", "3", "5", "7", "9"]

// Reduce cumulative operation
let result = odd.reduce(0, +)
print(result) / / 25

// flatMap 1
let numberList = [[1.2.3], [4.5], [[6]]]
let flatMapNumber = numberList.flatMap { (value) in
    return value
}
print(flatMapNumber) // [1, 2, 3, 4, 5, [6]]

// flatMap 2. Filter array for nil
let country = ["cn"."us".nil."en"]
let flatMap = country.flatMap { (value) in
    return value
}
print(flatMap) //["cn", "us", "en"]

// compactMap filters array nil
let compactMap = country.compactMap { (value) in
    return value
}
print(compactMap) // ["cn", "us", "en"]
Copy the code

Filter and reduce are easy to understand, while map, flatMap, and compactMap are easy to confuse at the beginning. They need more use and practice.

Note that flatMap has two uses, either to expand an array and reduce a two-dimensional array to a one-dimensional array, or to filter nil from an array. In Swift4.1 we deprecated the function flatMap to filter nil in arrays, so we should use compactMap to filter nil in arrays.

Reference: Swift Brain burning exercise (4) – map and flatMap

V. Several concepts in Swift

What does curryification mean

Currying refers to the transformation from a multi-parameter function to a series of single-parameter functions, which is an important tool for functional programming. Here’s an example:

// This function returns type (Int) -> Bool
func greaterThan(_ comparer: Int)- > (Int) - >Bool {
    return { number in
        return number > comparer
    }
}
// Define a function of greaterThan10
let greaterThan10 = greaterThan(10)
greaterThan10(13)    // => true
greaterThan10(9)     // => false
Copy the code

So currying can also be understood as generating a series of similar functions in batches.

Reference: CURRYING

POP 与 OOPThe difference between

Object-oriented Programming (OOP)

In the world of object-oriented programming, everything is object, its core ideas are inheritance, encapsulation, polymorphism.

Protocol oriented Programming (POP)

Protocol oriented programming defines a set of operations through a protocol, or interface. Protocols also have inheritance encapsulation polymorphisms, but these are not built for objects.

Why Swift evolved into a protocol-oriented programming language. This is because object-oriented has the following problems:

1. Dynamic dispatch security (this should be OC’s dilemma, it is impossible for Xcode to compile this problem in Swift)

2, cross-cutting Concerns Object-oriented cannot describe the fact that two different things have a certain property in common.

3. Diamond problem (e.g. in C++). C++ can have multiple inheritance. In multiple inheritance, two superclasses implement the same method, and the child class cannot determine which superclass inherits this method. Because the topology of multiple inheritance is a Diamond, this Problem is called the Diamond Problem.

Reference article:

Protocol Oriented Programming in Swift: Is it better than Object-oriented Programming?

Protocol-oriented Programming meets Cocoa (part 1)

Any 与AnyObjectThe difference between

AnyObject: is a protocol that all classes follow. This protocol is used to convert data to an OC object.

Any: It can represent classes, structs, enums of Any type, including functions and lectotypes, basically anything.

rethrows ε’Œ throwsWhat’s the difference?

Throws is used to handle errors. See an example of writing to a sandbox:

// Write the method definition
public func write(to url: URL, options: Data.WritingOptions = []) throws
/ / call
do {
    let data = Data(a)try data.write(to: localUrl)
} catch let error {
    print(error.localizedDescription)
}
Copy the code

Throws () throws () throws () throws () throws () throws () throws () throws () throws () throws () throws () throws () throws () throws () throws () throws () throws () throws

Rethrows are not much different from throws; they both indicate that a method should throw an error. But rethrows are usually used in higher-order functions that have methods that can be thrown as arguments. Answer next time).

Throws,rethrows ();

@inlinable public func map<T>(_ transform: (Element) throws -> T) rethrows- > [T]
Copy the code

When you first saw the map ontology, did you ever wonder why a closure in a map needs to throw an error? Why do we call it without the try syntax and it passes?

Here’s the thing: Transform is the closure that we need to define, and it may or may not throw an exception. Swift is a type-safe language that requires a try call when there is an exception, and a try call when there is no exception.

func squareOf(x: Int) -> Int {return x * x}

func divideTenBy(x: Int) throws -> Double {
    guardx ! =0 else {
        throw CalculationError.DivideByZero
    }
    return 10.0 / Double(x)
}

let theNumbers = [10.20.30]
let squareResult = theNumbers.map(squareOf(x:)) / / [100, 400, 9000]

do {
    let divideResult = try theNumbers.map(divideTenBy(x:))
} catch let error {
    print(error)
}
Copy the code

When we write let divideResult = thenumbers.map (divideTenBy(x:)) directly, the compiler reports an error: Call can throw but is not marked with ‘try’. This makes it possible to decide whether or not to use a try-catch to catch an exception in the map.

Reference: Error and exception handling

Break return continue fallThough (switch, while, for)

The switch statement in Swift will automatically exit the switch judgment at the end of each case. If we don’t want to exit and proceed to the next case judgment, we can add fallthough.