作为swift中的一种自定义类型,和struct,class,enum不同,我们使用protocol来定义某种约定,而不是一个具体的类型。这种约定通常用于表示某些类型的共性
注:本篇文章学习自泊学(boxueio.com)
Protocol
定义
protocol Engine // 所有遵从Engine约定的类型都必须同时提供start和stop这两种方法。
{
// -3. 属性
var cylinder: Int { get set } // 汽缸数 // ①
var capacity: Double {get} // 排量
// -1. 方法
func start()
func stop()
// -2. 参数:
// func getName(prefix: String = "")// ② 默认参数在protocol中是不被允许的
func getName(prefix: String)
func getName()
}
注释:
① 由于Engine并不是一个具体的类型,因此当我们在一个protocol中定义具体属性的时候,我们必须使用{ get set }这样的方法来明确指定该属性支持的操作。{ get set }表示可读写的,{ get }表示只读
② 当protocol中的方法带有参数时,参数是不能有默认值的;如果要支持默认值,我们只能再定义两个方法
应用
1)由于Engine只是一个约定,因此不能直接生成安全的对象
//let truck = Engine()
2)只能定义一个具体类型的struct,class,enum,让它们遵从Engine的约定,如:
class Truck: Engine {...} 这和类继承的方式很像,但是 当冒号:后是一个protocol时,表示类型Truck遵从protocol Engine的约束。此时会报错,这是因为,虽然我们声明了Truck遵从Engine的约定,但是我们并没有真正实现start和stop这两个方法
3)Protocol的继承
除了定义属性和方法之外,protocol还可以继承的,用于表示约定A也是一个约定B,eg:
protocol TurboEngine: Engine
{
// var text: Int {get}
func startTurbo()
func stopTurbo()
}
let v8: TurboEngine 表示v8不仅是一个TurboEngine,也是一个Engine
自定义类型遵从Protocol
1. 在自定义类型中实现Protocol中的方法
Protocol中的方法必须全部实现
class V8: TurboEngine{
// ******** 方法约定 ********* 必须实现
// ------ Engine methods -----
func start() {
print("Engine start")
}
func stop() {
print("Engine stop")
}
func getName(prefix: String) {
print("(prefix)-v8-engine")
}
func getName() {
print("v8-engine")
}
// ------- TurboEngine methods ------
func startTurbo() {
print("Turbo start")
}
func stopTurbo() {
print("Turbo stop")
}
}
2. 在自定义类型中实现Protocol中的属性
Protocol中的属性也必须全部实现
1)Protocol中可读写的属性
class V8: TurboEngine{
// ******** 属性约定 *********
var cylinder = 8
}
cylinder在protocol Engine里看起来像个computed property,但是在V8的实现里可以简单定义为stored property
let v8L40 = V8()
v8L40.cylinder // get
v8L40.cylinder = 18 // set. 因此,一个stored property是满足protocol中get和set约定的
2)Protocol中只读的属性
class V8: TurboEngine{
// V8实现的时候:
// 1. 可以定义一个常量 让capacity达到只读的效果;
// let capacity = 4.0 // 不是必须的
// 2. 让capacity在V8里面变成一个变量
// var capacity = 4.0
// 3. 使用computed property来实现protocol里capacity的约定。当我们使用computed property的时候,通常需要定义一个内部的stored property,eg:
private var innerCapacity = 4.0
// 然后定义一个computed property来实现capacity的约定
var capacity: Double {
get {
return self.innerCapacity
}// 尽管在protocol里面,capacity只有get属性,但是在V8的实现里同样可以给它添加set方法
set {
self.innerCapacity = newValue
}
}
} 这样 当一个变量的类型是V8的时候,刚才添加的capacity就是可写的,eg:
v8L40.capacity = 8.0
但是如果把v8L40的类型转换成Engine或TurboEngine,capacity就会变成一个只读的
3. 遵从多个Protocol
在class的声明里使用 "," 将protocol分开 表示遵从多个约定
class V8: TurboEngine,Motor{...}
Protocol与Extension
1. 为Protocol添加额外的默认功能
protocol Flight{ // 航班信息
var delay: Int { get } // 航班晚点的次数
var normal: Int { get } // 航班正常的次数
var flyHour: Int { get }// 航班飞行的总时长
} 1)拓展一个protocol 看似和拓展其他自定义类型没有太大的区别,
都是使用extension关键字 + 要拓展的类型名字。
extension Flight { // 和定义protocol不同,我们可以在一个protocol extension中提供默认的实现,eg:在这里可以把totalTrips定义成一个computed property
var totalTrips: Int { return delay + normal }
func test ()->String{
return "test"
}
}
2)尽管此时我们还没有定义任何遵从Flight的约定,但是已经可以在extension中使用Flight的数据成员了,
因为swift的编译器知道,任何一个遵从Flight的自定义类型 一定会定义Flight约定的各种属性。
3)定义一个表示空客A380客机的类型:
struct A380: Flight { // 遵从Flight protocol
var delay: Int // 添加Flight约定的三个属性
var normal: Int
var flyHour: Int
}
4)此时,当定义了一个A380对象之后,就可以使用totalTrips获取总的飞行次数了
et a380 = A380(delay: 300,normal: 700,flyHour: 3 * 365 * 24)
a380.totalTrips
a380.test() playground:
1000
"test"
2. 为已有的方法提供默认的实现
protocol Flight{ // 航班信息
var delay: Int { get } // 航班晚点的次数
var normal: Int { get } // 航班正常的次数
var flyHour: Int { get }// 航班飞行的总时长
// eg2:
func delayRate() -> Double
}
extension Flight {
var totalTrips: Int { return delay + normal }
func test ()->String{
return "test"
}
// eg2:
func delayRate() -> Double {
return Double(delay) / Double(totalTrips)
}
}
a380.delayRate()
3. 1和2的区别
通过extension添加到protocol中的内容不算做protocol的约定
1)在A380为delayRate添加一个自定义实现 ,让它返回0.1:
struct A380: Flight { // 遵从Flight protocol
var delay: Int // 添加Flight约定的三个属性
var normal: Int
var flyHour: Int
// eg3:
func delayRate() -> Double {
return 0.1
}
}
a380.delayRate()
(a380 as Flight).delayRate()
这时 无论flight1的类型是A380 还是Flight,delayRate的结果都会是0.1。
原因:我们在A380中重新定义了Flight中约定的方法
2)注释掉在Flight中的delayRate方法:
此时再次调用:
a380.delayRate()
(a380 as Flight).delayRate() flight1的类型是A380时,delayRate的结果是0.1;flight1的类型是Flight时,delayRate的结果是0.3。
原因:此时delayRate不再是Flight约定的一部分了,在Flight extension中的delayRate只不过是为Flight protocol提供的一个方法1的添加的额外默认功能,既然delayRate不再是Flight约定的一部分了,那么swift编译器也不会认为A380中重定义的delayRate是在重新实现Flight中的约定,而只会把A380中的delayRate当成是普通方法,因此当我们把flight1的类型转换为Flight时,swift就会调用Flight的delayRate,事实上Flight的和A380中定义的delayRate没有任何关系。
4. 为默认实现限定其可用的条件
1)同时满足两个Protocol的类型,才可实现的方法
2)eg:
protocol OperationalLife{
var maxFlyHours: Int { get }
}
3)然后使用extension来为同时满足Flight和OperationalLife这两个protocol类型添加一个新的方法
extension Flight where Self: OperationalLife{
func isInService() -> Bool {
return self.flyHour < maxFlyHours
}
} 1??使用关键字where来表示额外的遵从条件
2??关键字Self用来表示遵从Flight类型,要求它必须同时遵从OperationalLife
4)然后让A380遵从OperationalLife。
extension A380: OperationalLife {
var maxFlyHours: Int { return 18 * 365 * 24 }// 假定服务年限为18年
}
由于在extension里我们不能定义stored property,所以只能把maxFlyHours定义成一个computed property
5)此时a380就可以使用OperationalLife中定义的isInService方法了
a380.isInService()
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|