你可以使用 类型转换 中描述的 is
和 as
操作符来检查协议一致性,即是否遵循某协议,并且可以转换到指定的协议类型。检查和转换协议的语法与检查和转换类型是完全一样的:
is
用来检查实例是否遵循某个协议,若遵循则返回true
,否则返回false
;as?
返回一个可选值,当实例遵循某个协议时,返回类型为协议类型的可选值,否则返回nil
;as!
将实例强制向下转换到某个协议类型,如果强转失败,将触发运行时错误。
下面的例子定义了一个 HasArea
协议,该协议定义了一个 Double
类型的可读属性 area
:
protocol HasArea { var area: Double { get } }
如下所示,Circle
类和 Country
类都遵循了 HasArea
协议:
class Circle: HasArea { let pi = 3.1415927 var radius: Double var area: Double { return pi * radius * radius } init(radius: Double) { self.radius = radius } } class Country: HasArea { var area: Double init(area: Double) { self.area = area } }
Circle
类把 area
属性实现为基于存储型属性 radius
的计算型属性。Country
类则把 area
属性实现为存储型属性。这两个类都正确地遵循了 HasArea
协议。
如下所示,Animal
是一个未遵循 HasArea
协议的类:
class Animal { var legs: Int init(legs: Int) { self.legs = legs } }
Circle
,Country
,Animal
并没有一个共同的基类,尽管如此,它们都是类,它们的实例都可以作为 AnyObject
类型的值,存储在同一个数组中:
let objects: [AnyObject] = [ Circle(radius: 2.0), Country(area: 243_610), Animal(legs: 4) ]
objects
数组使用字面量初始化,数组包含一个 radius
为 2
的 Circle
的实例,一个保存了英国国土面积的 Country
实例和一个 legs
为 4
的 Animal
实例。
如下所示,objects
数组可以被迭代,并对迭代出的每一个元素进行检查,看它是否遵循 HasArea
协议:
for object in objects { if let objectWithArea = object as? HasArea { print("Area is \(objectWithArea.area)") } else { print("Something that doesn't have an area") } } // Area is 12.5663708 // Area is 243610.0 // Something that doesn't have an area
当迭代出的元素遵循 HasArea
协议时,将 as?
操作符返回的可选值通过可选绑定,绑定到 objectWithArea
常量上。objectWithArea
是 HasArea
协议类型的实例,因此 area
属性可以被访问和打印。
objects
数组中的元素的类型并不会因为强转而丢失类型信息,它们仍然是 Circle
,Country
,Animal
类型。然而,当它们被赋值给 objectWithArea
常量时,只被视为 HasArea
类型,因此只有 area
属性能够被访问。