Advanced operator

Overflow operators

A runtime error is thrown when an overflow occurs in Swift’s arithmetic operator

Swift has overflow operators &+, &-, &* to support overflow operations

var min = UInt8.min print(min &- 1) // 255, Int8.max var max = UInt8.max print(max &+ 1) // 0, Min print(Max &* 2) // 254, equivalent to Max &+ MaxCopy the code

calculation

It’s kind of a loop, 255 plus 1, it goes back to zero; Zero minus one, it goes back to 255

And Max &* 2 is equal to Max &+ Max, which is 255 + 1 + 254, 255 + 1 is going to be 0, so it’s going to be 254

Operator Overload

Classes, structs, and enumerations can provide custom implementations of existing operators, an operation called operator overloading

struct Point {
    var x: Int, y: Int
}

func + (p1: Point, p2: Point) -> Point {
    Point(x: p1.x + p2.x, y: p1.y + p2.y)
}

let p = Point(x: 10, y: 20) + Point(x: 11, y: 22)
print(p) // Point(x: 21, y: 42) Point(x: 11, y: 22)
Copy the code

Operator overloading is typically written inside related structures, classes, and enumerations

Struct Point {var x: Int, y: Int static func + (p1: Point, p2: Point) -> Point {Point(x: p1.x + p2.x, y: y) p1.y + p2.y) } static func - (p1: Point, p2: Point) -> Point { Point(x: p1.x - p2.x, y: P1.y-p2.y)} static prefix func - (p: Point) -> Point {Point(x: -p.x, y: -p.y)} static prefix func += (p1: inout Point, p2: Point) { p1 = p1 + p2 } static prefix func ++ (p: inout Point) -> Point { p += Point(x: 1, y: Static postfix func ++ (p: inout Point) -> Point {let TMP = p p += Point(x: 1, y: 1) 1) return tmp } static func == (p1: Point, p2: Point) -> Bool { (p1.x == p2.x) && (p1.y == p2.y) } } var p1 = Point(x: 10, y: 20) var p2 = Point(x: 11, y: 22) print(p1 + p2) // Point(x: 21, y: 42) print(p2 - p1) // Point(x: 1, y: 2) print(-p1) // Point(x: -10, y: -20) p1 += p2 print(p1, p2) // Point(x: 21, y: 42) Point(x: 11, y: 22) p1 = ++p2 print(p1, p2) // Point(x: 12, y: 23) Point(x: 12, y: 23) p1 = p2++ print(p1, p2) // Point(x: 12, y: 23) Point(x: 13, y: 24) print(p1 == p2) // falseCopy the code

Equatable

To know if two instances are equivalent, it is common to follow the Equatable protocol and override the == operator

At the same time, equivalent to! = operator.

class Person: Equatable { var age: Int init(age: Int) { self.age = age } static func == (lhs: Person, rhs: Person) -> Bool { lhs.age == rhs.age } } var p1 = Person(age: 10) var p2 = Person(age: 20) print(p1 == p2) // false print(p1 ! = p2) // trueCopy the code

If the Equatable protocol is not followed, use! = will report an error

The p1 == p2 judgment can also be used if the == operator is overloaded if the Equatable protocol is not complied with, but the Equatable protocol is complied with in order to be used as an argument in restricted generic functions

func equal<T: Equatable>(_ t1: T, _ t2: T) -> Bool {
    t1 == t2
}

print(equal(p1, p2)) // false
Copy the code

Swift provides defaults for the following typesEquatableimplementation

There is no enumeration of associated types

enum Answer {
    case right, wrong
}

var s1 = Answer.right
var s2 = Answer.wrong
print(s1 == s2)
Copy the code

Only enumerations that comply with the Equatable protocol association type are available

Many of the system’s native types already comply with the Equatable protocol, such as ints and doubles

enum Answer: Equatable {
    case right, wrong(Int)
}

var s1 = Answer.wrong(20)
var s2 = Answer.wrong(10)
print(s1 == s2)
Copy the code

An error is reported if the association type does not comply with the Equatable protocol

Only constructs that comply with the Equatable protocol store attributes are available

struct Point: Equatable { var x: Int, y: Int } var p1 = Point(x: 10, y: 20) var p2 = Point(x: 11, y: 22) print(p1 == p2) // false print(p1 ! = p2) // trueCopy the code

Reference types compare stored address values for equality (referencing the same object) using the identity operators ===,! = =

class Person { } var p1 = Person() var p2 = Person() print(p1 === p2) // false print(p1 ! == p2) // trueCopy the code

Comparable

To compare the sizes of two instances, it is common to comply with the Comparable protocol and override the corresponding operators

struct Student: Comparable { var age: Int var score: Int init(score: Int, age: Int) { self.score = score self.age = age } static func < (lhs: Student, rhs: Student) -> Bool { (lhs.score < rhs.score) || (lhs.score == rhs.score && lhs.age > rhs.age) } static func > (lhs: Student, rhs: Student) -> Bool { (lhs.score > rhs.score) || (lhs.score == rhs.score && lhs.age < rhs.age) } static func <= (lhs: Student, rhs: Student) -> Bool { ! (lhs > rhs) } static func >= (lhs: Student, rhs: Student) -> Bool { ! (lhs < rhs) } } var stu1 = Student(score: 100, age: 20) var stu2 = Student(score: 98, age: 18) var stu3 = Student(score: 100, age: 20) print(stu1 > stu2) // true print(stu1 >= stu2) // true print(stu1 >= stu3) // true print(stu1 <= stu3) // true print(stu2 < stu1) // true print(stu2 <= stu1) // trueCopy the code

Custom Operators

You can customize new operators by declaring them in a global scope using operator

Prefix operator Prefix operator Postfix operator suffix operator infix operator prefix operator priority group PRECEDenceGroup priority group {associativity: Left \right\ None: higherThan: lowerThan: Assignment: true: to have the same priority as the assignment operator in the optional chain operation}Copy the code

The following is an example of a custom operator

prefix operator +++

prefix func +++ (_ i: inout Int) {
    i += 2
}

var age = 10
+++age
print(age) // 12
Copy the code
infix operator +-: PlusMinusPrecedence
precedencegroup PlusMinusPrecedence {
    associativity: none
    higherThan: AdditionPrecedence
    lowerThan: MultiplicationPrecedence
    assignment: true
}

struct Point {
    var x = 0, y = 0
    static func +- (p1: Point, p2: Point) -> Point {
        Point(x: p1.x + p2.x, y: p1.y - p2.y)
    }
}

var p1 = Point(x: 10, y: 20)
var p2 = Point(x: 5, y: 10)

print(p1 +- p2) // Point(x: 15, y: 10)
Copy the code

Effect of setting associativity in priority groups

The three associativity options left: left to right for multiple associativity options Right: right to left for multiple associativity options None: multiple associativity options are not supportedCopy the code

If we add another calculation, we’ll get an error because we set associativity to None

We’ll change the associativity to left or right, and we’ll just run it

infix operator +-: PlusMinusPrecedence
precedencegroup PlusMinusPrecedence {
    associativity: left
    higherThan: AdditionPrecedence
    lowerThan: MultiplicationPrecedence
    assignment: true
}

var p3 = Point(x: 5, y: 10)
print(p1 +- p2 +- p3) // Point(x: 20, y: 0)
Copy the code

Effect of Setting Assignment in priority group

Let’s start with the following example code

class Person { var age = 0 var point: Point = Point() } var p: Person? = nil print(p? .point +- Point(x: 10, y: 20))Copy the code

Setting assignment to true means that if the preceding optionality in the operation is nil, then the code following the operator will not execute

Apple document reference links: developer.apple.com/documentati…

Another: docs.swift.org/swift-book/…

Extension

The basic concept

Extensions in Swift are similar to categories in OC

Extensions can add new functionality to enumerations, classes, structures, and protocols; You can add methods, convenient initializers, computed properties, subscripts, nested types, protocols, and more

Extensions do not do the following

  • Unable to override the original function
  • You cannot add storage properties or add property observers to existing properties
  • Cannot add a parent class
  • You cannot add a designated initializer, and you cannot add a de-initializer
  • .

Evaluate properties, methods, subscripts, nested types

Extension Double {var km: Double {self * 1_000.0} var m: Double {self} var dm: Double {self / 10.0} var cm: Double {self * 1_000.0} var m: Double {self} var dm: Double {self / 10.0} var cm: Double {self / 100.0} var mm: Double {self / 1_000.0}Copy the code
extension Array { subscript(nullable idx: Int) -> Element? { if (startIndex.. <endIndex).contains(idx) { return self[idx] } return nil } }Copy the code
extension Int { func repetitions(task: () -> Void) { for _ in 0.. <self { task() } } mutating func square() -> Int { self = self * self return self } enum Kind { case negative, zero, positive } var kind: Kind { switch self { case 0: return .zero case let x where x > 0: return .positive default: return .negative } } subscript(digitIndex: Int) -> Int { var decimalBase = 1 for _ in 0.. <digitIndex { decimalBase += 10 } return (self / decimalBase) % 10 } }Copy the code

The initializer

class Person { var age: Int var name: String init (age: Int, name: String) { self.age = age self.name = name } } extension Person: Equatable { static func == (left: Person, right: Person) -> Bool { left.age == right.age && left.name == right.name } convenience init() { self.init(age: 0, name: "")}}Copy the code

If you want to customize an initializer while the compiler generates a default initializer, you can write a custom initializer in an extension

struct Point {
    var x: Int = 0
    var y: Int = 0
}

extension Point {
    init(_ point: Point) {
        self.init(x: point.x, y: point.y)
    }
}

var p1 = Point()
var p2 = Point(x: 10)
var p3 = Point(y: 10)
var p4 = Point(x: 10, y: 20)
var p5 = Point(p4)
Copy the code

The required initializer also cannot be written to an extension

agreement

If a type has implemented all the requirements of the protocol, but has not yet declared compliance with the protocol, it can be extended to make it comply with the protocol

protocol TestProtocol {
    func test1()
}

class TestClass {
    func test1() {
        print("TestClass test1")
    }
}

extension TestClass: TestProtocol { }
Copy the code
extension BinaryInteger { func isOdd() -> Bool {self % 2 ! = 0 } } print(10.isOdd())Copy the code

Extensions can provide default implementations of protocols and indirectly implement the results of alternative protocols

Extensions can extend protocols to methods never declared in the protocol

protocol TestProtocol {
    func test1()
}

extension TestProtocol {
    func test1() {
        print("TestProtocol test1")
    }
    
    func test2() {
        print("TestProtocol test2")
    }
}

class TestClass: TestProtocol { }
var cls = TestClass()
cls.test1() // TestProtocol test1
cls.test2() // TestProtocol test2

var cls2: TestProtocol = TestClass()
cls2.test1() // TestProtocol test1
cls2.test2() // TestProtocol test2
Copy the code
class TestClass: TestProtocol {
    func test1() {
        print("TestClass test1")
    }
    
    func test2() {
        print("TestClass test2")
    }
}

var cls = TestClass()
cls.test1() // TestClass test1
cls.test2() // TestClass test2

var cls2: TestProtocol = TestClass()
cls2.test1() // TestClass test1
cls2.test2() // TestProtocol test2
Copy the code

The generic

class Stack<E> {
    var elements = [E]()
    func push(_ element: E) {
        elements.append(element)
    }
    
    func pop() -> E {
        elements.removeLast()
    }
    
    func size() -> Int {
        elements.count
    }
}
Copy the code

Extensions can still use generic types from the original type

extension Stack { func top() -> E { elements.last! }}Copy the code

Expand only if conditions are met

extension Stack: Equatable where E : Equatable {
    static func == (left: Stack, right: Stack) -> Bool {
        left.elements == right.elements
    }
}
Copy the code

Access Control

The basic concept

In the area of access control, Swift provides five different access levels (in descending order, entities refer to content modified by access levels)

  • Open: allows access in the module that defines the entity, in other modules, and allows other modules to inherit and overwrite (open can only be used on classes and class members)
  • Public: allows access in the module that defines the entity or in other modules. Other modules are not allowed to inherit or overwrite the entity
  • Internal: Access is only allowed in the module that defines the entity, not in other modules
  • Fileprivate: Access is only allowed in the source file that defines the entity
  • Private: Access is only allowed in closed declarations that define entities

Most entities default to the internal level

Guidelines for use of access levels

An entity may not be defined by an entity with a lower level of access

Variable \ constant type ≥ variable \ constant

Internal class Person {} // var Person: Person // variableCopy the code

Parameter type, return value type ≥ function

Function: func test internal func test(_ num: Int) -> Double {return Double(num)}Copy the code

Parent class ≥ subclass

class Person {}
class Student: Person {}
Copy the code
public class Person {}
class Student: Person {}
Copy the code

Parent protocol ≥ child protocol

public protocol Sportable {}
internal protocol Runnalbe: Sportable {}
Copy the code

Original type ≥ TypeAlias

Class Person {} private TypeAlias MyPerson = PersonCopy the code

Original value type \ Associated value type ≥ enumeration type

typealias MyInt = Int
typealias MyString = String

enum Score {
    case point(MyInt)
    case grade(MyString)
}
Copy the code

Other types used in defining type A ≥ type A

typealias MyString = String

struct Dog {}

class Person {
    var age: MyString = ""
    var dog: Dog?
}
Copy the code

A tuple type

The tuple type has the lowest level of access of all member types

Internal struct Dog {} fileprivate class Person {} So the access level of the tuple is fileprivate fileprivate var datal: (Dog, Person) Private var data2: (Dog, Person)Copy the code

The generic type

The access level of a generic type is the lowest of the access level of the type and the access level of all generic type parameters

internal class Car {} fileprivate class Dog {} public class Person<T1, T2> {} // Person<Car, Fileprivate var p = Person<Car, Dog>()Copy the code

Member, nested type

The access level of a type affects the default access level of a nested type for members (properties, methods, initializers, subscripts)

In general, if the type is private or Fileprivate, then the member \ nested type is also private or Fileprivate by default

fileprivate class FilePrivateClass { // fileprivate
    func f1() {} // fileprivate
    private func f2() {} // private
}

private class PrivateClass { // private
    func f() {} // private
}
Copy the code

In general, if the type is internal or public, the member \ nested type defaults to internal

public class PublicClass { // public
    public var p1 = 0 // public
    var p2 = 0 // internal
    fileprivate func f1() {} // fileprivate
    private func f2() {} // private
}

class InternalClass { // internal
    var p = 0 // internal
    fileprivate func f1() {} // fileprivate
    private func f2() {} // private
}
Copy the code

See the following examples, can the compilation pass?

Example 1

private class Person {}
fileprivate class Student: Person {}
Copy the code
class Test {
    private class Person {}
    fileprivate class Student: Person {}
}
Copy the code

The result is that the first code compiles and the second code compiles with an error

The first code compiles because both global variables, whether private or Fileprivate, are scoped to the current file, so the access level is the same

The second code limits the scope of the two attributes to the class, so the access level is different

Example 2


private struct Dog {
    var age: Int = 0
    func run() {}
}

fileprivate struct Person {
    var dog: Dog = Dog()
    mutating func walk() {
        dog.run()
        dog.age = 1
    }
}
Copy the code
private struct Dog {
    private var age: Int = 0
    private func run() {}
}

fileprivate struct Person {
    var dog: Dog = Dog()
    mutating func walk() {
        dog.run()
        dog.age = 1
    }
}
Copy the code

The result is that the first code compiles and the second code compiles with an error

The first code compiles because both structures have the same access level because they are inside the file

The second code fails because the access level of the properties and methods in Dog is lower. Although the two structures have the same access level, the properties and methods in Dog cannot be accessed by calling them from Person

Conclusion: Directly defined in global scopeprivateIs equal to thefileprivate

Rewriting of members

The access level of the overridden member of a subclass must be greater than or equal to the access level of the overridden member of the parent class

class Person {
    internal func run() {}
}

fileprivate class Student: Person {
    fileprivate override func run() {}
}
Copy the code

A member of a parent class cannot be overridden by a child class defined outside the member scope

Put it in the same scope

public class Person {
    private var age: Int = 0
    
    public class Student: Person {
        override var age: Int {
            set {}
            get { 10 }
        }
    }
}
Copy the code

Getter and setter

Getters and setters automatically receive the access level of their environment by default

Setters can be given a separate access level lower than getters to limit write permissions

fileprivate(set) public var num = 10
num = 10
print(num)
Copy the code

The initializer

If a public class wants to call the compile-generated default no-parameter initializer in another module, it must explicitly provide a public no-parameter initializer because the default initializer for public classes is internal

Public class Person {// Generated by default, because it is internal, // internal init() {// //}} public class Person {// Manually add the specified initializer and decorate it with public, Public init() {}}Copy the code

Required initializer ≥ its default access level

fileprivate class Person {
    internal required init() {}
}
Copy the code

When a class is public, its default initializer is internal, so no errors are reported

public class Person {
    internal required init() {}
}
Copy the code

If a structure has the storage instance property of private\ Fileprivate, its member initializer is also private\ Fileprivate, otherwise it defaults to internal

One property in the structure is set to private, and initializers with other properties are gone

Case of enumeration type

An access level cannot be set separately for each case of an enum

Each case automatically receives enum access level

fileprivate enum Season {
    case spring // fileprivate
    case summer // fileprivate
    case autumn // fileprivate
    case winter // fileprivate
}
Copy the code

Public enum Case defined is also public

public enum Season {
    case spring // public
    case summer // public
    case autumn // public
    case winter // public
}
Copy the code

agreement

The access level defined in the protocol requires the automatic receive protocol. The access level cannot be set separately

Public The requirements defined by the protocol are also public

public protocol Runnable {
    func run()
}
Copy the code

The access level of the protocol implementation must be greater than or equal to the access level of the type or protocol

extension

If the extension’s access level is explicitly set, members added to the extension automatically receive the extension’s access level

class Person {

}

private extension Person {
    func run() {} // private
}
Copy the code

If the access level of the extension is not explicitly set, the default access level for members added by the extension is the same as for members defined directly in the type

private class Person {
    
}

extension Person {
    func run() {} // private
}
Copy the code

Access levels can be set separately for members added to the extension

class Person {
    
}

extension Person {
    private func run() {} 
}
Copy the code

Extensions used for protocol compliance cannot be explicitly set to the access level of the extension

Extensions in the same file can be written as type declarations in multiple parts

Declare a private member in the original declaration that can be accessed in an extension of the same file

Declare a private member in an extension that can be accessed in other extensions of the same file, in the original declaration

public class Person {
    private func run0() {}
    private func eat0() {
        run1()
    }
}

extension Person {
    private func run1() {}
    private func eat1() {
        run0()
    }
}

extension Person {
    private func eat2() {
        run1()
    }
}
Copy the code

Assign the method to var\let

Methods can also be assigned to a let or var, like functions

struct Person {
    var age: Int
    func run(_ v : Int) { print("func run", age, v)}
    static func run(_ v: Int) { print("static func run", v)}
}

let fn1 = Person.run
fn1(10) // static func run 10

let fn2: (Int) -> () = Person.run
fn2(20) // static func run 20

let fn3: (Person) -> ((Int) -> ()) = Person.run
fn3(Person(age: 18))(30) // func run 18 30
Copy the code