本文首发于个人博客
前言
协议,有关开发经验的应该都不陌生,很多语言中都有协议,但是相对来说,Swift中的协议更加强大,灵活。
- Swift中协议可以用来定义方法、属性、下标的声明,协议可以被枚举、结构体、类遵守(多个协议之间用逗号隔开)
1 | //协议 |
类TestClass
准守多个协议
1 | protocol Test1 { } |
需要注意的是
- 协议中定义方法时不能有默认参数值
- 默认情况下,协议中定义的内容必须全部都实现
- 也有办法办到只实现部分内容,后面会说
协议中的属性
- 协议中定义属性时必须用var关键字
- 实现协议时的属性权限要不小于协议中定义的属性权限
- 协议定义get、set,用var存储属性或get、set计算属性去实现
- 协议定义get,用任何属性都可以实现
eg:有协议Drawable
,里面有方法draw
,以及可读可写属性x
,只读属性y
,下标。其中属性必须用var
关键字
1 | //协议 |
当实现的时候,有如下的方式,
1 | class Person : Drawable { |
当然了。也可以写成如下这种
1 | class Person : Drawable { |
static、class
- 为了保证通用,协议中必须用static定义类型方法、类型属性、类型下标
- 因为class只能用在类中,不能用于结构体等。所以为了通用,用static
- 但是实现的时候,可以用class,也可以用static,具体看自己的情况
eg:
1 | protocol Drawable { |
mutating
关于mutating
可以参考Swift之方法
- 只有将协议中的实例方法标记为mutating
- 才允许结构体、枚举的具体实现修改自身内存
- 类在实现方法时不用加mutating,枚举、结构体才需要加mutating
eg:
1 | protocol Drawable { |
init
- 协议中还可以定义初始化器init
- 非final类实现时必须加上required
可以这么理解,如果定义的类,有子类,那么子类必须准守初始化器init,所以加上关键字required
,但是,如果一个被final
修饰的类。就不用加上required
.因为被final
修饰的类不能被其他类继承。
关于final
可参考Swift之继承
eg: 有协议Drawable
,里面定义了初始化器init
,类Point遵守这个协议,所以在init 前面加了关键字 required
,这样,继承类Point的子类都要实现这个方法,但是类Size没子类。因为加了关键字final
,这个类不能被继承。所以init
前面不用加required
1 | protocol Drawable { |
- 如果从协议实现的初始化器,刚好是重写了父类的指定初始化器
- 那么这个初始化必须同时加required、override
eg:
1 | protocol Livable { |
init、init?、init!
- 协议中定义的init?、init!,可以用init、init?、init!去实现
- 协议中定义的init,可以用init、init!去实现
eg:
1 | //协议 |
协议的继承
- 一个协议可以继承其他协议
eg
1 | // 协议Runnable |
协议组合
- 协议组合,多个协议组合在一起,而且可以包含1个类类型(最多1个)
eg: 两个协议Livable
和Runnable
,类Person
1 | protocol Livable { } n |
下面定义了fn0,接收参数必须是Person或者其子类的实例。fn1接收参数必须遵守Livable协议的实例,其他的可以自行看代码
1 | // 接收Person或者其子类的实例 |
CaseIterable
- 让枚举遵守CaseIterable协议,可以实现遍历枚举值
1 | // 枚举Season遵守协议CaseIterable |
CustomStringConvertible
- 遵守CustomStringConvertible协议,可以自定义实例的打印字符串
eg: Person
类遵守了CustomStringConvertible
协议,可以再内部自定义打印description
。
1 | class Person : CustomStringConvertible { |
Any、AnyObject
- Swift提供了2种特殊的类型:Any、AnyObject
- Any:可以代表任意类型(枚举、结构体、类,也包括函数类型)
- AnyObject:可以代表任意类类型(在协议后面写上: AnyObject代表只有类能遵守这个协议)
eg: stu属于Any类型,可以赋值为字符串或者对象等
1 | var stu: Any = 10 |
eg: 创建1个能存放任意类型的数组
1 | // 创建1个能存放任意类型的数组 |
is、as?、as!、as
- is用来判断是否为某种类型,as用来做强制类型转换
eg: 定义协议Runnable
,类Person
和类Student
1 | protocol Runnable { func run() } |
使用is 的时候
1 | var stu: Any = 10 |
使用as 的时候,如果转换失败,后面都不执行。转换成功,后面才继续执行
1 | var stu: Any = 10 |
X.self、X.Type、AnyClass
- X.self是一个元类型(metadata)的指针,metadata存放着类型相关信息
- X.self属于X.Type类型
1 | // 定义类Person |
元类型的应用
eg:Cat
类,Dog
类,Pig
类,都继承自Animal
类,我们想同时去初始化,可使用下面的代码
1 | class Animal { required init() { } } |
注意上面的required不能省略
- 在OC、Java等语言中,任何一个类最终都要继承自某个基类,
- 在Swift中没有这个规定,如果一个类不继承任何一个类,那这个类就是基类
实际上,真的如此么,真的不继承任何类么?
1 | class Person { |
- 从结果可以看得出来,Swift还有个隐藏的基类:Swift._SwiftObject
- 可以参考Swift源码
Self
- Self一般用作返回值类型,限定返回值跟方法调用者必须是同一类型(也可以作为参数类型)
有点类似OC中的instanType的感觉。eg:
1 | protocol Runnable { |
调用时候
1 | var p = Person() |
参考资料: