- 协议定义了一组方法、属性、以及其他特定的任务需求。
- 通过协议,可以定义接口,而不必关心具体的实现。
- 协议可被类、结构体、或枚举类型遵循。
一、协议的语法
- 协议定义
protocol SomeProtocol { }
- 多个协议用逗号分开
struct SomeStruct: FirstProtocol, AnotherProtocol { }
- 若一个类拥有父类,将父类名放在协议名之前
class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol { }
二、属性要求
- 属性必须明确是 可读的{ get }或 可读的和可写的{ get set }。
- 属性要求定义为变量属性,在名称前面使用 var 关键字。
- 协议中定义类型属性时必须在前面添加 static 关键字。
protocol SomeProtocol {
var mustBeSettable: Int { get set }
var doesNotNeedToBeSettable: Int { get }
}
示例一: 定义一个具有只读属性的协议。
- 首先定义一个协议 FullyNamed,FullyNamed 定义了一个只读属性 fullName。
- Person 结构体遵循了 FullyNamed 协议。
protocol FullyNamed {
var fullName: String { get }
}
struct Person: FullyNamed {
var fullName: String
}
var john = Person(fullName: "John Appleseed")
john.fullName = "John Doe" // 更改 fullName
print(john.fullName) // 打印:John Doe
fullName不是只读属性吗?为什么不报错?
然而,fullName属性 只在协议 FullyNamed 中被定义为只读属性 { get };
在结构体 Person 中,它是一个实际的存储属性(即可以读写),具有默认的读写能力,因此你可以对它进行赋值。
示例二:定义一个带有类型属性的协议 static
protocol Animal {
static var species: String { get }
func weight() -> Double
}
struct Dog: Animal {
static var species: String {
return "犬科"
}
func weight() -> Double {
return 10.0
}
}
struct Cat: Animal {
static var species: String {
return "猫科"
}
func weight() -> Double {
return 2.5
}
}
// 使用类型属性
print("Dog species: \(Dog.species)") // Dog species: 犬科
print("Cat species: \(Cat.species)") // Cat species: 猫科
// 使用实例属性
let dog = Dog()
let cat = Cat()
print("dog weight: \(dog.weight())kg") // dog weight: 10.0kg
print("cat weight: \(cat.weight())kg") // cat weight: 2.5kg
三、方法要求
- 协议可以实现实例方法和类方法;
- 方法参数不能定义默认值;
- 定义类方法时,同样需要在其之前添加 static 关键字;
protocol SomeProtocol {
// 实例方法
func someMethod()
}
protocol SomeProtocol {
// 类方法
static func someTypeMethod()
}
异变方法
- 在方法的 func 关键字之前使用 mutating 关键字,来表示在该方法可以改变实例的属性,这样的方法被称为异变方法。
- mutating 方法允许你在方法中修改结构体或枚举的属性。
- mutating 并不适用于属性本身的定义,它用于方法中。
示例一:结构体
protocol FullyNamed {
var fullName: String { get set }
}
struct Person: FullyNamed {
var fullName: String
mutating func updateFullName(to newName: String) {
self.fullName = newName
}
}
var john = Person(fullName: "John Appleseed")
print(john.fullName) // 打印:John Appleseed
john.updateFullName(to: "John Doe")
print(john.fullName) // 打印:John Doe
示例二:枚举
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 // .off
lightSwitch.toggle() // .on
四、初始化器要求
协议可以要求遵循协议的类型 实现指定的初始化器;
protocol SomeProtocol {
init(someParameter: Int)
}
当一个协议要求类实现一个初始化器时,必须使用 required 关键字标记这个初始化器,这样它才能被子类继承。
示例一:使用指定初始化器满足协议要求
protocol Animal {
init(species: String)
}
class Dog: Animal {
var species: String
required init(species: String) {
self.species = species
}
}
class GuideDog: Dog {
var name: String
init(species: String, name: String) {
self.name = name
super.init(species: species)
}
required init(species: String) {
self.name = "没起名"
super.init(species: species)
}
}
let dog = GuideDog(species: "导盲犬", name: "大黄")
print(dog.species) // 导盲犬
print(dog.name) // 大黄
示例二:使用便捷初始化器实现协议要求
protocol Shape {
init(name: String)
}
class Rectangle: Shape {
var name: String
required init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "默认形状")
}
}
let shape = Rectangle()
print(shape.name) // 默认形状
let rectangle = Rectangle(name: "矩形")
print(rectangle.name) // 矩形
如果一个子类重写了父类指定的初始化器,并且遵循协议实现了初始化器要求,那么就要为这个初始化器的实现添加 required 和 override 两个修饰符:
protocol SomeProtocol {
init()
}
class SomeSuperClass {
init() {
}
}
class SomeSubClass: SomeSuperClass, SomeProtocol {
required override init() {
}
}
五、将协议作为类型
协议自身并不实现功能; 由于它是一个类型,你可以在很多其他类型可以使用的地方使用协议;
- 在函数、方法或初始化器里作为形式参数类型 或 返回类型;
- 作为常量、变量或者属性的类型;
- 作为数组、字典等存储类型;
示例:
// RandomNumberGenerator 协议要求采用该协议的类型都必须有一个实例方法 random,而且返回一个 Double 的值;
protocol RandomNumberGenerator {
func random() -> Double
}
// 遵循 RandomNumberGenerator 协议的类的实现
// linear congruential generator 著名的伪随机数算法
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
}
}
// 这个例子定义了一个 Dice 的新类,他表示一个n面骰子
// sides 表示有多少个面
// generator 提供了随机数的生成器来生成骰子的值
class Dice {
let sides: Int
let generator: RandomNumberGenerator // 属性,RandomNumberGenerator类型
// 形参,RandomNumberGenerator类型
init(sides: Int, generator: RandomNumberGenerator) {
self.sides = sides
self.generator = generator
}
func roll() -> Int {
return Int(generator.random() * Double(sides)) + 1
}
}
// 使用
var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
for _ in 1...5 {
print("Random dice roll is \(d6.roll())")
}
/**
打印:
Random dice roll is 3
Random dice roll is 5
Random dice roll is 4
Random dice roll is 5
Random dice roll is 4
*/
六、协议委托
协议委托(或代理模式)是一种常用的设计模式,在Swift中用于实现对象之间的松耦合和事件通知。
通过使用委托,类可以将某些任务或事件委托给其他类,从而提高代码的模块化和可维护性;
示例:假设我们有一个下载任务类 Downloader,它负责下载数据。 当下载完成,我们希望通知某个对象,进行相应的处理。
protocol DownloaderDelegate: AnyObject {
func downloadDidFinish()
func downloadDidFail()
}
class Downloader {
weak var delegate: DownloaderDelegate?
func startDownload() {
// 模拟一个下载过程
let success = Bool.random()
if success {
// 下载成功
delegate?.downloadDidFinish()
} else {
// 下载失败
delegate?.downloadDidFail()
}
}
}
class ViewController: DownloaderDelegate {
func downloadDidFinish() {
print("下载成功")
}
func downloadDidFail() {
print("下载失败")
}
func startDownloadTask() {
let downloader = Downloader()
downloader.delegate = self
downloader.startDownload()
}
}
通过协议委托模式,可以将任务和事件处理分离,使代码更加模块化和易于维护。
七、协议的继承
协议可以继承一个或多个其他协议,从而扩展和组合它们的功能;
类似于类的继承,但协议可以多重继承,类只能单继承;
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
// protocol definition goes here
}
示例:
protocol Animal {
var species: String { get }
func makeSound() -> String
}
protocol Pet: Animal {
var name: String { get }
func play() -> String
}
class Dog: Pet {
var species: String
var name: String
init(species: String, name: String) {
self.species = species
self.name = name
}
func makeSound() -> String {
return "🐶woo~"
}
func play() -> String {
return "\(name) 在玩呢"
}
}
使用:
let myDog = Dog(species: "犬科", name: "大黄")
print(myDog.species) // 犬科
print(myDog.makeSound()) // 🐶woo~
print(myDog.name) // 大黄
print(myDog.play()) // 大黄 在玩呢
在这个示例中:
-
Pet 协议继承了 Animal 协议,因此任何遵循 Pet 协议的类型也必须实现 Animal 协议中的要求。
-
Dog 类实现了 Pet 协议,因此它需要实现 Animal 和 Pet 协议中的所有属性和方法。
八、协议的组合
协议组合允许你在某个类型需要 同时遵循多个协议 时,将多个协议组合在一起使用。
用符号 & 连接,用逗号分隔。
示例
protocol Named {
var name: String { get }
}
protocol Aged {
var age: Int { get }
}
struct Person: Named, Aged {
var name: String
var age: Int
}
func wishHappyBirthday(to celebrator: Named & Aged) {
print("恭喜\(celebrator.name)\(celebrator.age)岁生日快乐!")
}
let person = Person(name: "小明", age: 20)
wishHappyBirthday(to: person) // 恭喜小明20岁生日快乐!
九、协议的扩展
协议可以通过扩展来提供方法和属性的实现; 这就允许你在协议中自定义行为;
例如,RandomNumberGenerator 协议可以扩展来提供 randomBool() 方法。
extension RandomNumberGenerator {
func randomBool() -> Bool {
return random() > 0.5
}
}
十、协议遵循的检查
使用 is 和 as 运算符来检查协议遵循。
- is 运算符返回true 或 false;
- as? 不遵循这个协议的话值就是 nil;
- as! 强制转换失败 触发运行时错误;
if dog is Animal {
print("遵守了Animal协议")
}
if let dog = object as? Animal {
print("遵守了Animal协议")
} else {
print("dog = nil")
}
// 如果强制转换失败 **触发运行时错误**
if let dog = object as! Animal {
print("遵守了Animal协议")
}
十一、协议的可选实现
在 Swift 中,协议中的要求通常是强制性的,但有时我们希望某些协议要求是 可选的。
为此,Swift提供了两个主要的方法:
- 通过 Objective-C 的特性实现可选协议要求;
- 在协议扩展中提供默认实现;
使用Objective-C特性实现可选协议要求
将协议标记为 @objc optional,这种方法只能在 类 中使用(因为Objective-C特性 不支持 结构体 或 枚举)。
示例
@objc protocol OptionalProtocol {
var requiredProperty: String { get }
func requiredMethod()
// 可选属性和方法
@objc optional var optionalProperty: String? { get }
@objc optional func optionalMethod()
}
class MyClass: OptionalProtocol {
var requiredProperty: String = "requiredProperty"
func requiredMethod() {
print("required Method")
}
// 可选方法,可以选择不实现
func optionalMethod() {
print("optional Method")
}
}
let myObject = MyClass()
print(myObject.requiredProperty) // requiredProperty
myObject.requiredMethod() // required Method
myObject.optionalMethod() // optional Method
在协议扩展中提供默认实现
protocol DefaultProtocol {
func requiredMethod()
func optionalMethod()
var optionalProperty: String { get }
}
extension DefaultProtocol {
func optionalMethod() {
print("扩展实现了 optionalMethod")
}
var optionalProperty: String {
return "扩展实现了 optionalProperty"
}
}
class MyClass: DefaultProtocol {
func requiredMethod() {
print("实现了 requiredMethod")
}
// 可选方法和属性可以选择重写默认实现
func optionalMethod() {
print("重写默认实现 optionalMethod")
}
var optionalProperty: String = "重写默认实现 optionalProperty"
}
let myObject = MyClass()
myObject.requiredMethod() // 实现了 requiredMethod
myObject.optionalMethod() // 重写默认实现 optionalMethod
print(myObject.optionalProperty) // 重写默认实现 optionalProperty
参考Apple官方Swift教程:点击链接