作为swift中的一种自定义类型,和struct,class,enum不同,我们使用protocol来定义某种约定,而不是一个具体的类型。这种约定通常用于表示某些类型的共性@H_404_3@
注:本篇文章学习自泊学(boxueio.com)@H_404_3@
@H_404_3@
@H_404_3@ Protocol
定义
@H_404_3@
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 }表示只读@H_404_3@
② 当protocol中的方法带有参数时,参数是不能有默认值的;如果要支持默认值,我们只能再定义两个方法@H_404_3@
@H_404_3@
应用
1)由于Engine只是一个约定,因此不能直接生成安全的对象@H_404_3@
@H_404_3@
//let truck = Engine()
@H_404_3@
2)只能定义一个具体类型的struct,class,enum,让它们遵从Engine的约定,如:@H_404_3@
总结@H_404_3@
class Truck: Engine {...}这和类继承的方式很像,但是 当冒号:后是一个protocol时,表示类型Truck遵从protocol Engine的约束。此时会报错,这是因为,虽然我们声明了Truck遵从Engine的约定,但是我们并没有真正实现start和stop这两个方法
3)Protocol的继承@H_404_3@
除了定义属性和方法之外,protocol还可以继承的,用于表示约定A也是一个约定B,eg:
@H_404_3@@H_404_3@
protocol TurboEngine: Engine{// var text: Int {get} func startTurbo() func stopTurbo()}let v8: TurboEngine表示v8不仅是一个TurboEngine,也是一个Engine
@H_404_3@ 自定义类型遵从Protocol1. 在自定义类型中实现Protocol中的方法Protocol中的方法必须全部实现@H_404_3@
@H_404_3@
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中的属性也必须全部实现
@H_404_3@1)Protocol中可读写的属性@H_404_3@
@H_404_3@
class V8: TurboEngine{ // ******** 属性约定 ********* var cylinder = 8}cylinder在protocol Engine里看起来像个computed property,但是在V8的实现里可以简单定义为stored property
let v8L40 = V8()v8L40.cylinder // getv8L40.cylinder = 18 // set.因此,一个stored property是满足protocol中get和set约定的
@H_404_3@2)Protocol中只读的属性@H_404_3@
3. 遵从多个Protocol@H_404_3@
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就会变成一个只读的
@H_404_3@
@H_404_3@Protocol与Extension在class的声明里使用 "," 将protocol分开 表示遵从多个约定
@H_404_3@@H_404_3@
class V8: TurboEngine,Motor{...}
@H_404_3@1. 为Protocol添加额外的默认功能@H_404_3@
protocol Flight{ // 航班信息 var delay: Int { get } // 航班晚点的次数 var normal: Int { get } // 航班正常的次数 var flyHour: Int { get }// 航班飞行的总时长}1)拓展一个protocol 看似和拓展其他自定义类型没有太大的区别,都是使用extension关键字 + 要拓展的类型名字。@H_404_3@
@H_404_3@
extension Flight { // 和定义protocol不同,我们可以在一个protocol extension中提供默认的实现,eg:在这里可以把totalTrips定义成一个computed property var totalTrips: Int { return delay + normal } func test ()->String{ return "test" }}2)尽管此时我们还没有定义任何遵从Flight的约定,但是已经可以在extension中使用Flight的数据成员了,@H_404_3@
因为swift的编译器知道,任何一个遵从Flight的自定义类型 一定会定义Flight约定的各种属性。@H_404_3@
3)定义一个表示空客A380客机的类型:@H_404_3@
@H_404_3@
struct A380: Flight { // 遵从Flight protocol var delay: Int // 添加Flight约定的三个属性 var normal: Int var flyHour: Int}4)此时,当定义了一个A380对象之后,就可以使用totalTrips获取总的飞行次数了@H_404_3@
@H_404_3@
et a380 = A380(delay: 300,normal: 700,flyHour: 3 * 365 * 24)a380.totalTripsa380.test()playground:@H_404_3@
1000"test"
@H_404_3@2. 为已有的方法提供默认的实现@H_404_3@
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的区别@H_419_439@ 通过extension添加到protocol中的内容不算做protocol的约定
1)在A380为delayRate添加一个自定义实现 ,让它返回0.1:@H_404_3@
@H_404_3@
struct A380: Flight { // 遵从Flight protocol var delay: Int // 添加Flight约定的三个属性 var normal: Int var flyHour: Int // eg3: func delayRate() -> Double { return 0.1 }}@H_404_3@
a380.delayRate()(a380 as Flight).delayRate()
这时 无论flight1的类型是A380 还是Flight,delayRate的结果都会是0.1。原因:我们在A380中重新定义了Flight中约定的方法
@H_404_3@
2)注释掉在Flight中的delayRate方法:@H_404_3@
此时再次调用:@H_404_3@
@H_404_3@ @H_267_502@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没有任何关系。
@H_404_3@
@H_404_3@4. 为默认实现限定其可用的条件1)同时满足两个Protocol的类型,才可实现的方法@H_404_3@
2)eg:@H_404_3@
@H_404_3@
protocol Operationallife{ var maxFlyHours: Int { get }}3)然后使用extension来为同时满足Flight和Operationallife这两个protocol类型添加一个新的方法@H_404_3@
@H_404_3@
extension Flight where Self: Operationallife{ func isInService() -> Bool { return self.flyHour < maxFlyHours }}1️⃣使用关键字where来表示额外的遵从条件
2️⃣关键字Self用来表示遵从Flight类型,要求它必须同时遵从Operationallife4)然后让A380遵从Operationallife。@H_404_3@
@H_404_3@
extension A380: Operationallife { var maxFlyHours: Int { return 18 * 365 * 24 }// 假定服务年限为18年}由于在extension里我们不能定义stored property,所以只能把maxFlyHours定义成一个computed property@H_404_3@
5)此时a380就可以使用Operationallife中定义的isInService方法了@H_404_3@
@H_404_3@
a380.isInService()
以上是内存溢出为你收集整理的Swift中的protocol全部内容,希望文章能够帮你解决Swift中的protocol所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)