1. 协议语法
protocol SomeProtocol {
// code
}
要让自定义类型遵循某个协议,在类型名称后加上协议名称即可,中间以冒号(:
)分隔,遵循多个协议时,各协议之间用逗号(、
)分隔:
struct SomeStruct: FirstProtocol, AnotherProtocol {
// code
}
拥有父类的类在遵循协议时,应该将父类名放在协议名之前,以逗号分隔:
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
// code
}
2. 属性要求
protocol SomeProtocol {
var mustBeSettable: Int { get set } //可读可写的变量属性
var doesNotNeedToBeSettable: Int { get } //可读变量属性
}
protocol AnotherProtocol {
static var someTypeProperty: Int { get set } //类型属性,还可以用class关键字声明
}
如下所示,这是一个只含有一个实例属性要求的协议。这个协议表示,任何遵循FullyNamed
的类型,都必须有一个可读的String
类型的实力属性fullName
。
protocol FullyNamed {
var fullName: String { get }
}
下面是一个遵循FullyNamed
协议的简单结构体:
struct Person: FullyNamed {
var fullName: String
}
let john = Person(fullName: "John Higgins")
下面是一个更为复杂的类,它适配并遵循了FullyNamed
协议:
class StarShip: FullyNamed {
var prefix: String?
var name: String
init(name: String, prefix: String? = nil) {
self.name = name
self.prefix = prefix
}
var fullName: String {
return (prefix != nil ? prefix! + " " : "") + name
}
}
var ncc1701 = StarShip(name: "Enterprise", prefix: "USS")
print(ncc1701.fullName)
// print "USS Enterprise"
3. 方法要求
protocol SomeProtocol {
static func someTypeMethod() // 类方法还可以用class关键字修饰
}
下面的例子定义了一个只含有一个实例方法的协议:
protocol RandomNumberGenerator {
func random() -> Double
}
下面的类实现了一个叫做线性同余生成器的伪随机数算法:
class LinearCongruentialGenerator: RandomNumberGenerator {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
func random() -> Double {
lastRandom = (lastRandom * a + c).truncatingRemainder(dividingBy: m)
return lastRandom / m
}
}
4. 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
}
}
}
var lightSwitch = OnOffSwitch.Off
lightSwitch.toggle() // On
5. 构造器要求
protocol SomeProtocol {
init(someParameter: Int)
}
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// 构造器实现部分
}
}
如果一个子类重写了父类的指定构造器,并且该构造器满足了某个协议的要求,那么该构造器的实现需要同时标注required
和override
修饰符:
class SomeSuperClass {
init() {
// 构造器实现部分
}
}
class SomeSubClass: SomeSuperClass, SomeProtocol {
required override init() {
// 构造器实现部分
}
}
6. 协议作为类型
class Dice {
let sides: Int
let generator: RandomNumberGenerator
init(sides: Int, generotor: RandomNumberGenerator) {
self.sides = sides
self.generator = generator
}
func roll() -> Int {
return Int(generator.random() * Double(sides)) + 1
}
}
generator
属性的类型为RandomNumberGenerator
,因此任何遵循了RandomNumberGenerator
协议的类型的实例都可以赋值给generator
,除此之外并无其他要求。
7. 委托(代理)模式
protocol SayHelloDelegate {
func sayHello(name: String)
}
class ClassA {
var delegate: SayHelloDelegate?
var name = "Lucy"
func play() {
delegate?.sayHello(name: name)
}
}
class ClassB: SayHelloDelegate {
var name = "Lily"
func sayHello(name: String) {
print("\(name) 请 \(self.name) 帮她 say Hello")
}
}
var ca = ClassA()
var cb = ClassB()
ca.delegate = cb
ca.play()
// print "Lucy 请 Lily 帮她 say Hello"
8. 协议中添加扩展
protocol Score {
var math: Int { get set }
var english: Int { get set }
func mathPercent() -> Double
}
struct Puple: Score {
var math: Int
var english: Int
func mathPercent() -> Double {
return Double(math) / Double(math + english)
}
}
let p1 = Puple(math: 90, english: 80)
p1.mathPercent() // 0.529
extension Score {
func mathPercent() -> Double {
return Double(math) / Double(math + english)
}
}
struct CollageStudent: Score {
var math: Int
var english: Int
}
let c1 = CollageStudent(math: 80, english: 80)
c1.mathPercent() // 0.5
如此,我们可以不实现mathPercent
方法也能计算出数学所占分数的比例。