- 协议的语法(Protocol Syntax)
- 对属性的规定(Property Requirements)
- 对方法的规定(Method Requirements)
- 对突变方法的规定(Mutating Method Requirements)
- 对构造器的规定(Initializer Requirements)
- 协议类型(Protocols as Types)
- 在扩展中添加协议成员(Adding Protocol Conformance with an Extension)
- 通过扩展补充协议声明(Declaring Protocol Adoption with an Extension)
- 协议的继承(Protocol Inheritance)
- 类专属协议(Class-Only Protocol)
- 协议合成(Protocol Composition)
- 检验协议的一致性(Checking for Protocol Conformance)
- 对可选协议的规定(Optional Protocol Requirements)
协议的语法(Protocol Syntax)
协议
的定义:
protocol SomeProtocol { // 协议内容 }
实现多个协议,各协议之间用逗号分隔:
struct SomeStructure: FirstProtocol, AnotherProtocol { // 结构体内容 }
如果一个类在含有父类
的同时也采用了协议,应当把父类
放在所有的协议
之前,如下所示:
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol { // 类的内容 }
对属性的规定(Property Requirements)
protocol SomeProtocol { var mustBeSettable : Int { get set } // 可读可写的 var doesNotNeedToBeSettable: Int { get } // 只读 }
在协议中定义类成员,使用class作为前缀,在枚举和结构体中实现协议时,需要使用static
关键字作为前缀。
protocol AnotherProtocol { class var someTypeProperty: Int { get set } }
对方法的规定(Method Requirements)
定义类方法和定义属性一样使用class属性,枚举、结构体实现时需要用static属性
protocol SomeProtocol { class func someTypeMethod() // 类方法 func random() -> Double // 实例方法 }
该协议规定要求实现者要有一个someTypeMethod的类方法,且没有参数没有返回值。还有一个random()的方法,返回一个Double类型的值。
对变异方法的规定(Mutating Method Requirements)
用类
实现协议中的mutating
方法时,不用写mutating
关键字;用结构体
,枚举
实现协议中的mutating
方法时,必须写mutating
关键字。
protocol Togglable {
mutating func toggle()
}
enum OnOffSwitch: Togglable { case Off, On mutating func toggle() { // 枚举实现的变异方法 switch self { case Off: self = On case On: self = Off } } }
对构造器的规定(Initializer Requirements)
protocol SomeProtocol {
init(someParameter: Int)
}
该协议要求实现者必须实现一个int参数的构造器
class SomeClass: SomeProtocol { required init(someParameter: Int) { // 必须加上 required 修饰符 //构造器实现 } }
必须给构造器实现标上"required"修饰符,使用required
修饰符可以保证:所有的遵循该协议的子类,同样能为构造器规定提供一个显式的实现或继承实现。如果,该类被final修饰了,那就不用再加required修饰符,因为被final修饰的类不能有子类
如果一个子类重写了父类的指定构造器,并且该构造器遵循了某个协议的规定,那么该构造器的实现需要被同时标示required
和override
修饰符
protocol SomeProtocol { init() } class SomeSuperClass { init() { //协议定义 } } class SomeSubClass: SomeSuperClass, SomeProtocol { // "required" 来自于集成的协议; "override" 来自于父类 required override init() { // 构造器实现 } }
协议类型(Protocols as Types)
协议
可以被当做类型来使用
使用场景:
协议类型
作为函数、方法或构造器中的参数类型或返回值类型协议类型
作为常量、变量或属性的类型协议类型
作为数组、字典或其他容器中的元素类型
在扩展中添加协议成员(Adding Protocol Conformance with an Extension)
扩展可以为已存在的类型添加属性
,方法
,下标脚本
,协议
等成员。
TextRepresentable
协议含有一个asText
,如下所示:
protocol TextRepresentable { func asText() -> String }
通过扩展
为上一节中提到的Dice
类遵循TextRepresentable
协议
extension Dice: TextRepresentable { func asText() -> String { return "A \(sides)-sided dice" } }
从现在起,Dice
类型的实例可被当作TextRepresentable
类型:
let d12 = Dice(sides: 12,generator: LinearCongruentialGenerator()) println(d12.asText()) // 输出 "A 12-sided dice"
SnakesAndLadders
类也可以通过扩展
的方式来遵循协议:
extension SnakesAndLadders: TextRepresentable { func asText() -> String { return "A game of Snakes and Ladders with \(finalSquare) squares" } } println(game.asText()) // 输出 "A game of Snakes and Ladders with 25 squares"
通过扩展补充协议声明(Declaring Protocol Adoption with an Extension)
当一个类型已经实现了协议中的所有要求,却没有声明时,可以通过扩展
来补充协议声明:
struct Hamster { var name: String func asText() -> String { return "A hamster named \(name)" } } extension Hamster: TextRepresentable {}
Hamster
的实例就可以作为TextRepresentable
类型使用
let simonTheHamster = Hamster(name: "Simon") let somethingTextRepresentable: TextRepresentable = simonTheHamster println(somethingTextRepresentable.asText()) // 输出 "A hamster named Simon"
协议的继承(Protocol Inheritance)
协议能够多继承。语法与类的继承相似,多个协议间用逗号分隔:
protocol InheritingProtocol: SomeProtocol, AnotherProtocol { // 协议定义 }
类专属协议(Class-Only Protocol)
你可以在协议的继承列表中,通过添加“class”关键字,限制协议由类来实现。
protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol { // class-only protocol definition goes here }
在以上例子中,协议SomeClassOnlyProtocol只能被类(class)类型适配。如果尝试让结构体或枚举类型适配该协议,则会出现编译错误。
注意: 当协议需求定义的行为,要求(或假设)它的遵循类型必须是引用类型而非值类型时,应该采用类专属协议。
协议合成(Protocol Composition)
一个协议可由多个协议采用protocol<SomeProtocol, AnotherProtocol>
这样的格式进行组合,称为协议合成
。
protocol Named { var name: String { get } } protocol Aged { var age: Int { get } } struct Person: Named, Aged { var name: String var age: Int } func wishHappyBirthday(celebrator: protocol<Named, Aged>) { println("Happy birthday \(celebrator.name) - you're \(celebrator.age)!") } let birthdayPerson = Person(name: "Malcolm", age: 21) wishHappyBirthday(birthdayPerson) // 输出 "Happy birthday Malcolm - you're 21!
wishHappyBirthday方法表示接收一个实现了Named和Aged的类型的实例
注意: 协议合成
并不会生成一个新协议类型,而是将多个协议合成为一个临时的协议,超出范围后立即失效。
检验协议的一致性(Checking for Protocol Conformance)
使用is
和as
操作符来检查协议的一致性或转化协议类型。
is
操作符用来检查实例是否遵循
了某个协议
as?
返回一个可选值,当实例遵循
协议时,返回该协议类型;否则返回nil
as
用以强制向下转型。转型失败时会抛出异常
@objc protocol HasArea { var area: Double { get } }
注意: @objc
用来表示协议是可选的,也可以用来表示暴露给Objective-C
的代码,此外,@objc
型协议只对类
有效,因此只能在类
中检查协议的一致性。
如下所示,定义了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 } } // 没有实现HasArea协议的Animal类 class Animal { var legs: Int init(legs: Int) { self.legs = legs } } let objects: [AnyObject] = [ Circle(radius: 2.0), Country(area: 243_610), Animal(legs: 4) ] for object in objects { if let objectWithArea = object as? HasArea { println("Area is \(objectWithArea.area)") } else { println("Something that doesn't have an area") } } // Area is 12.5663708 // Area is 243610.0 // Something that doesn't have an area
对可选协议的规定(Optional Protocol Requirements)
可选协议含有可选成员,其遵循者
可以选择是否实现这些成员。在协议中使用@optional
关键字作为前缀来定义可选成员。可选协议在调用时使用可选链
像someOptionalMethod?(someArgument)
这样,你可以在可选方法名称后加上?
来检查该方法是否被实现。可选方法
和可选属性
都会返回一个可选值(optional value)
,当其不可访问时,?
之后语句不会执行,并整体返回nil
注意: 可选协议只能在含有@objc
前缀的协议中生效。且@objc
的协议只能被类
遵循
@objc protocol CounterDataSource { optional func incrementForCount(count: Int) -> Int // 可以选择是否实现 optional var fixedIncrement: Int { get } // 可以选择是否实现 }
2015-03-25
20:14:22