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 definition
The get and set
withvar
Store attributes orThe get and set
Compute properties to implement - Protocol definition
get
, 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