# Swift 协议探究 - 协议可以用来定义属性、方法、下标的声明,协议可以被类、枚举、结构体遵守(多个协议用逗号隔开) - 协议中定义的方法不能有默认参数值 - 协议中定义属性必须是 `var` - 实现协议时定义的属性权限,要不小于协议中定义的属性权限 - 协议定义属性是 `get`、`set` 时,用 `var` 存储属性或者 `get`、`set` 计算属性实现 - 为了保证通用,协议中必须用 `static` 定义类型方法、类型属性、类型下标 - 只有将协议中的实例方法标记为 `mutating` - 才可以允许结构体、枚举对象在方法里修改自身内存。否则编译器会报错:`Cannot assign to property: 'self' is immutable` - 类遵循协议,实现方法的时候不用加 `mutating`,枚举、结构体的实现需要加 `mutating` ```swift protocol Drawable { func draw() } class Size: Drawable { var width: Int = 0 func draw() { width = 10 } } var size = Size() print(size.width) // 0 size.draw() print(size.width) // 10 struct Point: Drawable { var x : Int = 0 var y: Int = 0 func draw() { x = 10 // Cannot assign to property: 'self' is immutable y = 10 // Cannot assign to property: 'self' is immutable } } ``` 要想修改需要加 `mumating` ```swift struct Point: Drawable { var x : Int = 0 var y: Int = 0 mutating func draw() { x = 10 y = 10 } } var point = Point() print(point.x, point.y) point.draw() print(point.x, point.y) ``` - 协议中还可以定义初始化器 `init`,非 `final` 类实现协议时, `init` 方法必须加 `required` ```swift protocol Drawable { init(x: Int, y: Int) } class Point: Drawable { var x: Int = 0 var y: Int = 0 required init(x: Int, y: Int) { self.x = x self.y = y } } final class Size: Drawable { var x: Int = 0 var y: Int = 0 init(x: Int, y: Int) { self.x = x self.y = y } } var point = Point(x: 10, y: 20) print(point.x , point.y) // 10 20 var size = Size(x: 30, y: 40) print(size.x , size.y) // 30 40 ``` - 如果协议声明了初始化器,某个类遵循协议并实现了初始化器。且该初始化器也恰好是父类指定初始化器,那么这个初始化必须同事加 `required` 和 `override` ```swift protocol Drawable { init(x: Int, y: Int) } class Shape { init(x: Int, y: Int) {} } class Circle: Shape, Drawable { var x: Int = 0 var y: Int = 0 required override init(x: Int, y: Int) { super.init(x: x, y: y) self.x = x self.y = y } } var circle = Circle(x: 10, y: 20) print(circle.x , circle.y) // 10 20 ``` - 协议也可以继承 - 协议也可以组合 ```swift protocol Drawable {} protocol Colorable {} func test1(obj: Shape) {} // 参数接收 Shape 类或者 Shape 类的子类 func test2(obj: Drawable) {} // 参数接收遵循 Drawable 的实例 func test3(obj: Drawable & Colorable) {} // 参数接收同时遵循 Colorable 和 Drawable 2个协议的实例 func test4(obj: Shape & Drawable & Colorable) {} // 参数接收同时遵循 Colorable 和 Drawable 2个协议,且是 Shape 的子类的实例 ``` - 遵循 `CustomStringConvertible` 可以自定义打印的字符串内容 ```swift class Person: CustomStringConvertible { var name: String var age: Int init(name: String, age: Int) { self.name = name self.age = age } var description: String { "My name is \(name), age is \(age)" } } var p = Person(name: "杭城小刘", age: 28) print(p) // My name is 杭城小刘, age is 28 ``` ## Any、AnyObject Swift 提供了2种特殊的类型:Any、AnyObject - Any 可以代表任意类型(枚举、结构体、类、函数类型) - AnyObject:代表任意类类型。比如可以在协议后面加上 AnyObject 则代表只有类能遵循这个协议。编译器会做检查 `Non-class type 'point' cannot conform to class protocol 'Eatable'` ```swift protocol Eatable: AnyObject {} class Person: Eatable { } struct point: Eatable {} // Non-class type 'point' cannot conform to class protocol 'Eatable' ``` ## 关联类型 关联类型的作用:给协议中用到的类型,定义一个占位名称 协议中可以拥有多个关联类型 ```swift protocol Stackable { associatedtype Element mutating func push(_ element: Element) mutating func pop() -> Element func top() -> Element func size() -> Int } class Stack: Stackable { var elements = Array() func push(_ element: Element) { elements.append(element) } func pop() -> Element { elements.removeLast() } func top() -> Element { elements.last! } func size() -> Int { elements.count } } ``` ## Swift 范型本质 ```swift func swapValue(_ value1: inout T, _ value2: inout T) { (value1, value2) = (value2, value1) } var i1 = 11 var i2 = 22 swapValue(&i1, &i2) var s1 = "Hello" var s2 = "world" swapValue(&s1, &s2) ``` 在 `swapValue(&i1, &i2)` 处下断点可以看到下面的汇编代码: 可以看到: - 在第一处调用 `swapValue ` 方法的时候,将8字节的 metadata 信息保存到 `rdx` 寄存器了。也就是在调用 `swapValue` 方法的时候,分别将 `i1`(0x000000000000000b,也就是11)的地址值赋值给 rdi 寄存器,将 `i2`(0x0000000000000016,也就是22)的地址值赋值给 rsi 寄存器 - 将 `Int` 的 `metadata` 赋值给 `rdx` 寄存器 - 然后调用 `swapValue` 方法 - 后续的 `String` 的 `SwapValue` 过程类似 所以编译器最后在执行的时候,会将范型真正的类型对应的 metadata 信息当作函数参数,传递进去,再去执行函数 ## 范型类型约束 范型必须遵循协议,可以在方法后加 `<>`,在 `<>` 内写范型 `T: 需要继承的类 & 协议` (也可以是其他名字,大多数语言都写 T), ```swift protocol Runable {} class Person {} func swapValue(_ a: inout T, _ b: inout T) { (a, b) = (b, a) } ``` 另一种场景是在方法参数是某个范型且遵循协议后,对其范型有更多限制,则用 `where` 去处理。如下例子 ```swift protocol Stackable { associatedtype Element: Equatable } class Stack: Stackable { typealias Element = E } func equal(_ s1: S1, _ s2: S2)-> Bool where S1.Element == S2.Element, S1.Element: Hashable { return true } let s1 = Stack() let s2 = Stack() let s3 = Stack() var result:Bool = equal(s1, s2) print(result) // true result = equal(s1, s3) // Global function 'equal' requires the types 'Stack.Element' (aka 'Int') and 'Stack.Element' (aka 'String') be equivalent print(result) ```