swift 的协议以及不透明类型
概念
协议
协议定义了一个接口集合,用来声明某一特定任务或者功能的方法、属性,以及其他需要的东西。在swift语法中,类、结构体以及枚举都可以遵循协议,并实现协议接口中的方法。协议在面向接口、切片化、组件化等设计中起着至关重要的作用。由于在objective-c中没有多继承,因此,也会通过协议来实现面向接口化编程。
不透明类型
不透明类型的函数或方法的返回值,不是一个具体的类型,而是返回一个协议描述的类型。但与正常的返回协议描述的类型不同,不透明类型在实现时,返回的类型必须是唯一的,而正常返回协议类型的方法,则可以返回所有遵循此协议的对象。
使用方式说明
协议的声明场景,与普通类的方式十分相似。
protocol ShapeProctocol {
func drawShape()
}
protocol PatterProctocol {
var name: String? { get }
}
protocol CircularProctocol: ShapeProctocol, PatterProctocol {
func getCircularCenter() -> CGPoint
}
通过上述代码可以看出,协议在声明时,可以定义方法和属性,而且一种协议,可以遵循一种或者多种协议。协议在面向接口编程中起着重要的作用,swift的开源项目swinject就是采用协议的方式,对各组件进行解耦,OC,swift中都没有严格意义上的抽象类,协议就充当着这种角色。
在iOS 开发中,协议-代理的模式也经常在一对一传值、函数方法回调中使用,相对于block,协议-代理的模式通过方法回调,方法书写可以更加集中。很多UIKit的框架都是使用这种方法,例如:UIScrollView,UITableview,值得注意的是,大多数代理属性被声明时,在ARC环境下,都是使用weak修饰,以防止内存泄漏,但是CAAnimation的delegate是使用strong修饰的。
/* The delegate of the animation. This object is retained for the
* lifetime of the animation object. Defaults to nil. See below for the
* supported delegate methods. */
@property(nullable, strong) id <CAAnimationDelegate> delegate;
在讨论协议与不透明类型的时候,大体上,都是在讨论,两者作为返回值时有什么样的区别,或者说,两者在补充什么样的设计漏洞。使用协议作为返回值,可以使接口的抽象及通用型变得最大化,但同时,返回类型不确定,也就意味着很多依赖返回类型信息的操作就无法执行,举个🌰:
protocol TravelToolType { /* ... */ }
struct TheTaxiCar: TravelToolType {
var carNumber: Int
}
struct TheSubWay: TravelToolType { /* ... */ }
public func favoriteTravelTool(withNumber travelNumber: Int) -> TravelToolType {
if likeTaxi {
return TheTaxiCar(carNumber: travelNumber)
}
return TheSubWay()
}
这种实现方法,接口不对外暴漏实现方式,调用方只要清楚协议中定义了哪些方法,根据需要使用即可,类似于一种抽象工程的模式,非常好用。但是一旦我们的返回对象需要使用==来进行比较,情况就会发生变化:
let weekTravel = favoriteTravelTool(withNumber: 100)
let workTravel = favoriteTravelTool(withNumber: 100)
print(weekTravel == workTravel) // Binary operator '==' cannot be applied to two 'TravelToolType' operands
产生这种错误可能由于很多原因,但是报错提示的直接原因是没有包含Equatable协议的声明,于是我们尝试加入Equatable协议的声明,结果如下:
protocol TravelToolType: Equatable { /* ... */ }
struct TheTaxiCar: TravelToolType {
var carNumber: Int
}
struct TheSubWay: TravelToolType { /* ... */ }
// ---> Protocol 'TravelToolType' can only be used as a generic constraint because it has Self or associated type requirements
public func favoriteTravelTool(withNumber travelNumber: Int) -> TravelToolType {
if likeTaxi {
return TheTaxiCar(carNumber: travelNumber)
}
return TheSubWay()
}
新的问题来了,这里说一下我个人的理解解释,其中的隐喻可能不够精准,希望可以得到及时的补充,修改。由于返回协议是类似于一个隐蔽的盒子,里面装的是什么其实并不知道,而swift中==的使用,需要准确的知道盒子装的东西到底是猫罐头还是狗粮,也就是说 ,我们必须要知道返回的实体类型是什么。而现在并没有确定里面的类型,因此产生此类问题。
在关于不透明类型概念的介绍中,我们说到,不透明类型返回的某一个协议类型,怎么理解呢?同样是一个隐蔽的盒子,我们不知道里面装着什么,但是一旦加上了some关键字,就类似于给生产出来的盒子打上了生产标签,尽管我们仍然不知道盒子里到底是什么,但是我们知道这个生产的流水线只能生产出单一类型的某个产品,比如,只能生产出猫罐头或者狗粮。因此,使用不透明类型作为返回类型,就能够明确地表达所需要的API契约。修改后完整代码如下:
protocol TravelToolType: Equatable { /* ... */ }
struct TheTaxiCar: TravelToolType {
var carNumber: Int
}
struct TheSubWay: TravelToolType { /* ... */ }
public func favoriteTravelTool(withNumber travelNumber: Int) -> some TravelToolType {
return TheTaxiCar(carNumber: travelNumber)
}
let weekTravel = favoriteTravelTool(withNumber: 100)
let workTravel = favoriteTravelTool(withNumber: 100)
print(weekTravel == workTravel)
* true *\