Protocol

The basic concept

Protocols can be used to define declarations of methods, attributes, and subscripts. Protocols can be followed by structures, classes, and enumerations

Protocol Drawable {func draw() var x: Int {get set} var y: Int {get} subscript(index: Int) -> Int { get set } }Copy the code

Multiple protocols are separated by commas

protocol Test1 { }
protocol Test2 { }
protocol Test3 { }

class TestClass: Test1, Test2, Test3 { }
Copy the code

Methods defined in protocols cannot have default parameter values

By default, everything defined in the protocol must be implemented

Properties in the protocol

The var keyword must be used to define attributes in the protocol

When implementing the protocol, the property permissions must be no less than those defined in the protocol

  • Protocol definitionThe get and setwithvarStore attributes orThe get and setCompute properties to implement
  • Protocol definitionget, can be implemented with any attribute
protocol Drawable {
    func draw()
    var x: Int { get set }
    var y: Int { get }
    subscript(index: Int) -> Int { get set }
}

class Person1: Drawable {
    var x: Int = 0
    let y: Int = 0
    
    func draw() {
        print("Person1 draw")
    }
    
    subscript(index: Int) -> Int {
        set { }
        get { index }
    }
}

class Person2: Drawable {
    var x: Int {
        get { 0 }
        set { }
    }
    
    var y: Int { 0 }
    
    func draw() {
        print("Person2 draw")
    }
    
    subscript(index: Int) -> Int {
        set { }
        get { index }
    }
}

class Person3: Drawable {
    var x: Int {
        get { 0 }
        set { }
    }
    
    var y: Int {
        get { 0 }
        set { }
    }
    
    func draw() {
        print("Person3 draw")
    }
    
    subscript(index: Int) -> Int {
        set { }
        get { index }
    }
}
Copy the code

The static, class

To ensure generality, the protocol must use static to define type methods, type attributes, and type subscripts

protocol Drawable {
    static func draw()
}

class Person1: Drawable {
    static func draw() {
        print("Person1 draw")
    }
}

class Person2: Drawable {
    class func draw() {
        print("Person2 draw")
    }
}
Copy the code

mutating

The implementation of a structure or enumeration is allowed to modify its own memory only if the instance method in the protocol is marked mutating

Classes implement methods without mutating. Structures and enumerations need mutating

protocol Drawable {
    mutating func draw()
}

class Size: Drawable {
    var width: Int = 0
    
    func draw() {
        width = 10
    }
}

struct Point: Drawable {
    var x: Int = 0
    mutating func draw() {
        x = 10
    }
}
Copy the code

init

The protocol can also define an initializer init, which must be required for non-final class implementations

The goal is for all classes that comply with this protocol to have initializers, so adding required forces subclasses to be implemented, except for classes with final that do not need to be subclassed

protocol Drawable {
    init(x: Int, y: Int)
}

class Point: Drawable {
    required init(x: Int, y: Int) {
        
    }
}

final class Size: Drawable {
    init(x: Int, y: Int) {
        
    }
}
Copy the code

If the initializer implemented from the protocol overrides the specified initializer of the parent class, the initializer must also be required and override

protocol Livable {
    init(age: Int)
}

class Person {
    init(age: Int) { }
}

class Student: Person, Livable {
    required override init(age: Int) {
        super.init(age: age)
    }
}
Copy the code

Init as defined in the protocol? And init! , you can use init, init? And init! To achieve

protocol Livable { init() init? (age: Int) init! (no: Int) } class Person1: Livable { required init() { } required init? (age: Int) { } required init! (no: Int) { } } class Person2: Livable { required init() { } required init! (age: Int) { } required init? (no: Int) { } } class Person3: Livable { required init() { } required init(age: Int) { } required init(no: Int) { } }Copy the code

Init, init! To achieve

protocol Livable { init() init? (age: Int) init! (no: Int) } class Person4: Livable { required init! () { } required init? (age: Int) { } required init! (no: Int) { } }Copy the code

Inheritance of agreement

One protocol can inherit from another

protocol Runnable {
    func run()
}

protocol Livable: Runnable {
    func breath()
}

class Person: Livable {
    func breath() {
        
    }
    
    func run() {
        
    }
}
Copy the code

Protocol combinations

A protocol combination can contain a class type

Protocol Runnable {} protocol Livable {} class Person {} // Receiving instances of Person or subclasses func fn0(obj: Person) {} // Receive instance func fn1(obj: Livable) {} // Receive instance func fn2(obj: Livable) {} Livable & Runnable) {} // To receive both Livable and Runnable protocols, and be an instance of Person or its subclasses func fn3(obj: Person & Livable & Runnable) { } typealias RealPerson = Person & Livable & Runnable func fn4(obj: RealPerson) { }Copy the code

CaseIterable

Making enumerations conform to the CaseIterable protocol allows you to iterate over an enumeration value

enum Season: CaseIterable {
    case spring, summer, autumn, winter
}

let seasons = Season.allCases
print(seasons.count)

for season in seasons {
    print(season)
} // spring, summer, autumn, winter
Copy the code

CustomStringConvertible

Keep CustomStringConvertible, CustomDebugStringConvertible agreement, can be custom print string instance

class Person: CustomStringConvertible, CustomDebugStringConvertible {
    var age = 0
    var description: String { "person_\(age)" }
    var debugDescription: String { "debug_person_\(age)" }
}

var person = Person()
print(person) // person_0
debugPrint(person) // debug_person_0
Copy the code

Print calls description of the CustomStringConvertible protocol

DebugPrint, Po is invoked debugDescription CustomDebugStringConvertible agreement

Any, AnyObject

Swift provides two special types, Any and AnyObject

Any can represent Any type (enumerations, structs, classes, and even function types)

var stu: Any = 10
stu = "Jack"
stu = Size()
Copy the code
Var data = [Any]() data.append(1) data.append(3.14) data.append(Size()) data.append("Jack") data.append({10}) var data = [Any]() data.append(1) data.append(3.14) data.Copy the code

AnyObject can represent any class type

Write AnyObject after the protocol to indicate that only the class complies with the protocol

After the protocol, write “class” to indicate that only the class complies with the protocol

Is, as

Is is used to determine whether the type is a certain type

protocol Runnable {
    func run()
}

class Person { }

class Student: Person, Runnable {
    func run() {
        print("Student run")
    }
    
    func study() {
        print("Student study")
    }
}

var stu: Any = 10
print(stu is Int) // true

stu = "Jack"
print(stu is String) // true

stu = Student()
print(stu is Person) // true
print(stu is Student) // true
print(stu is Runnable) // true
Copy the code

As is used for casting

protocol Runnable { func run() } class Person { } class Student: Person, Runnable { func run() { print("Student run") } func study() { print("Student study") } } var stu: Any = 10 (stu as? Student)? .study() // Do not call study stu = Student() (stu as? Student)? .study() // Student study (stu as! Student).study() // Student study (stu as? Runnable)? .run() // Student runCopy the code
Var data = [Any]() data.append(Int("123") as Any) var d = 10 as Double print(d) // 10.0Copy the code

Yuan type

X.self

Elf is a pointer to a metatype, and metadata stores type-specific information

X. elf belongs to x. type

class Person { }

class Student: Person { }

var perType: Person.Type = Person.self
var stuType: Student.Type = Student.self
perType = Student.self

var anyType: AnyObject.Type = Person.self
anyType = Student.self

var per = Person()
perType = type(of: per)
print(Person.self == type(of: per)) // true
Copy the code

The essence of AnyClass is anyObject.type

var anyType2: AnyClass = Person.self
anyType2 = Student.self
Copy the code

The application of meta-types

class Animal { required init() { } } class Cat: Animal { } class Dog: Animal { } class Pig: Animal { } func create(_ clses: [Animal.Type]) -> [Animal] { var arr = [Animal]() for cls in clses { arr.append(cls.init()) } return arr } print(create([Cat.self, Dog.self, Pig.self]) // a1 = Animal() var t = Animal. Self var a2 = t.init() var a3 = Animal a4 = Animal.self()Copy the code

Self

Self represents the current type

class Person {
    var age = 1
    static var count = 2
    
    func run() {
        print(self.age)
        print(Self.count)
    }
}
Copy the code

Self is typically used as a return value type, specifying that the return value and method caller must be of the same type (can also be used as a parameter type)

protocol Runnable {
    func test() -> Self
}

class Person: Runnable {
    
    required init() {
        
    }
    
    func test() -> Self {
        type(of: self).init()
    }
}

class Student: Person {
    
}

var p = Person()
print(p.test()) // test_enum.Person

var stu = Student()
print(stu.test()) // test_enum.Student
Copy the code

The nature of metatype

Can we disassemble to see what the implementation of metatype looks like

var p = Person()
var pType = Person.self
Copy the code

We find that the last address value stored in the global variable pType is the address from which the call started

By printing, we find that the value of pType is the address value of the first 8 bytes of the Person instance object, which is the class information

Let’s look at the sample code below

var p = Person()
var pType = type(of: p)
Copy the code

Through analysis, we can see that type(of: p) is not a function call in nature, but only stores the first 8 bytes of the Person instance object into pType, which also proves that the nature of metatype is to store class information

We can also get Swift’s hidden base class _TtCs12_SwiftObject as follows

class Person {
    var age: Int = 0
}

class Student: Person {
    var no: Int = 0
}

print(class_getInstanceSize(Student.self)) // 32
print(class_getSuperclass(Student.self)!) // Person
print(class_getSuperclass(Student.self)!) // _TtCs12_SwiftObject
print(class_getSuperclass(NSObject.self)) // nil
Copy the code

We can look at the Swift source code to analyze this type

SwiftObject also has an ISA pointer inside it