协议
定义了一个蓝图,规定了用来实现某一特定任务或者功能的方法、属性,以及其他需要的东西。类、结构体 或枚举都可以遵循协议,并为协议定义的这些要求提供具体实现。
协议语法
协议的定义方式与类、结构体和枚举的定义非常相似:
protocol SomeProtocol{
//这里是协议的定义部分
}
要让自定义类型遵循某个协议,在定义类型是,需要在类型名称后加上协议名称,中间:
分隔,遵循多个协议时,各个协议之间用,
分隔。
struct SomeStructure: FirstProtocol, AnotherProtocol {
// 这里是结构体的定义部分
}
拥有父类的类在遵循协议时,应该将父类名放在协议名之前,以逗号分隔
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
// 这里是类的定义部分
}
属性要求
协议
可以要求遵循协议的类型提供特定名称
和类型的实例属性
或类型属性
。协议不指定属性是存储属性
还是计算属性
。它只指定属性的名称和类型。此外,协议还指定属性的可读的还是可读可写的。
如果协议要求属性是可读可写的,那么该属性不能是常量属性或只读的计算型属性。如果协议只要求属性是可读的,那么该属性不仅可以是可读的,如果代码需要的话,还可以是可写的。
协议总是用var
关键字来声明变量属性,在类型声明后加上{set get}
来表示属性是可读可写的,可读属性则用{get}
来表示。
protocol SomeProtocol {
var a : Int { get set }
var b : Int { get }
}
在协议中定义类型属性时,总是使用static
关键字作为前缀,当类类型遵循协议时,除了static
关键字,还可以使用class
关键字类声明类型属性。
protocol AnotherProtocol {
static var someTypeProperty : Int {get set};
}
方法要求
协议可以要求遵循协议的类型实现某些指定的实例方法或类方法。这些方法作为协议的一部分,像普通方法一样放在协议的定义中,但是不需要大括号和方法体,可以在协议中定义具有可变参数的方法,和普通方法的定义方式相同。但是,不支持为协议中的方法的参数提供默认值。
在协议中定义类方法的时候,总是使用static
关键字作为前缀。当类类型遵循协议 时,除了 static
关键字,还可以使用class
关键字作为前缀。
protocol SomeProtocol {
static func someTypeMethod()
}
下面的例子定义了一个只含有一个实例方法的协议:
protocol RandomNumberGenerator {
func random() -> Double
}
RandomNumberGenerator
协议要求遵循协议的类型必须拥有一个名为random
, 返回值类型为Double
的实例 方法。
RandomNumberGenerator
协议并不关心每一个随机数是怎样生成的,它只要求必须提供一个随机数生成器。
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) % m)
return lastRandom / m
}
}
let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
// 打印 “Here's a random number: 0.37464991998171”
print("And another one: \(generator.random())")
// 打印 “And another one: 0.729023776863283”
Mutating方法要求
有时需要在方法中改变方法所属的实例。例如,在值类型(即结构体和枚举)的实例方法中,将 mutating
关键 字作为方法的前缀,写在 func
关键字之前,表示可以在该方法中修改它所属的实例以及实例的任意属性的值。
如果你在协议中定义了一个实例方法,该方法会改变遵循该协议的类型的实例,那么在定义协议时需要在方法前 加 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()
// lightSwitch 现在的值为 .On
Togglable
协议只要求实现一个名为 toggle
的实例方法。根据名称的暗示,toggle()
方法将改变实例属性,从而切换遵循该协议类型的实例的状态。
当使用枚举或结构体来实现 Togglable
协议时,需要提供一个带有mutating
前缀的 toggle()
方法。
一个名为 OnOffSwitch
的枚举。这个枚举在两种状态之间进行切换,用枚举成员On
和 Off
表 示。枚举的toggle()
方法被标记为 mutating
,以满足 Togglable
协议的要求:
构造器要求
协议可以要求遵循协议的类型实现指定的构造器。在协议的定义里写下构造器的声明,但不需要写在花括号和构造器的实体。
protocol SomeProtocol {
init(someParameter: Int)
}
- 构造器要求在类中的实现
可以在遵循协议的类中实现构造器,无论作为指定构造器
,还是作为便利构造器
。你都必须为构造器标上required
修饰符。
使用required
修饰符可以确保所有子类也必须提供此构造器实现,从而也能符合协议。
protocol SomeProtocol {
init()
}
class SomeSuperClass {
init() {
// 这里是构造器的实现部分
}
}
class SomeSubClass: SomeSuperClass, SomeProtocol {
// 因为遵循协议,需要加上 required
// 因为继承自父类,需要加上 override
required override init() {
// 这里是构造器的实现部分
}
}
如果一个子类重写了父类的指定构造器,并且该构造器满足了某个协议的要求,那么该构造器的实现需要同时标 注 required
和override
修饰符:
委托(代理)模式
委托是一种设计模式,它允许类或结构体将一些需要它们负责的功能委托给其他类型的实例。委托模式的实现很简单:定义协议来封装那些需要被委托的功能,这样就能确保遵循协议的类型能提供这些功能。委托模式可以用来响应特定的动作,或者接收外部数据源提供的数据,而无需关心外部数据源的类型。
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
//B代理A去实现方法
ca.play();
//lucy 请 lily 帮她 say Hello