mirror of
https://github.com/NohamR/knowledge-kit.git
synced 2026-05-24 20:00:37 +00:00
7.1 KiB
7.1 KiB
Swift 协议探究
-
协议可以用来定义属性、方法、下标的声明,协议可以被类、枚举、结构体遵守(多个协议用逗号隔开)
-
协议中定义的方法不能有默认参数值
-
协议中定义属性必须是
var -
实现协议时定义的属性权限,要不小于协议中定义的属性权限
-
协议定义属性是
get、set时,用var存储属性或者get、set计算属性实现 -
为了保证通用,协议中必须用
static定义类型方法、类型属性、类型下标 -
只有将协议中的实例方法标记为
mutating- 才可以允许结构体、枚举对象在方法里修改自身内存。否则编译器会报错:
Cannot assign to property: 'self' is immutable - 类遵循协议,实现方法的时候不用加
mutating,枚举、结构体的实现需要加mutating
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 } }要想修改需要加
mumatingstruct 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方法必须加requiredprotocol 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和overrideprotocol 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 -
协议也可以继承
-
协议也可以组合
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可以自定义打印的字符串内容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'
protocol Eatable: AnyObject {}
class Person: Eatable { }
struct point: Eatable {} // Non-class type 'point' cannot conform to class protocol 'Eatable'
关联类型
关联类型的作用:给协议中用到的类型,定义一个占位名称
协议中可以拥有多个关联类型
protocol Stackable {
associatedtype Element
mutating func push(_ element: Element)
mutating func pop() -> Element
func top() -> Element
func size() -> Int
}
class Stack<Element>: Stackable {
var elements = Array<Element>()
func push(_ element: Element) {
elements.append(element)
}
func pop() -> Element {
elements.removeLast()
}
func top() -> Element {
elements.last!
}
func size() -> Int {
elements.count
}
}
Swift 范型本质
func swapValue<T>(_ 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),
protocol Runable {}
class Person {}
func swapValue<T: Person & Runable>(_ a: inout T, _ b: inout T) {
(a, b) = (b, a)
}
另一种场景是在方法参数是某个范型且遵循协议后,对其范型有更多限制,则用 where 去处理。如下例子
protocol Stackable {
associatedtype Element: Equatable
}
class Stack<E: Equatable>: Stackable {
typealias Element = E
}
func equal<S1: Stackable, S2: Stackable>(_ s1: S1, _ s2: S2)-> Bool
where S1.Element == S2.Element, S1.Element: Hashable {
return true
}
let s1 = Stack<Int>()
let s2 = Stack<Int>()
let s3 = Stack<String>()
var result:Bool = equal(s1, s2)
print(result) // true
result = equal(s1, s3) // Global function 'equal' requires the types 'Stack<Int>.Element' (aka 'Int') and 'Stack<String>.Element' (aka 'String') be equivalent
print(result)