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 typesEquatable
implementation
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 scopeprivate
Is 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