在 Swift 中,协议(Protocol)是一种为方法、属性和其他需求定义蓝图的方式。协议本身并不实现这些需求,它只是向遵循协议的类型(类、结构体、枚举等)规定这些需求应该存在并实现。
协议用来定义一组接口,这些接口可以被多个不同类型遵循并实现,从而实现多态性和接口分离、灵活扩展的目标。
定义协议 #
你可以用 protocol
关键字来定义协议,协议可以包含方法、属性、下标等需求。
protocol Drawable {
func draw()
}
protocol Identifiable {
var id: String { get }
}
在这个例子中,Drawable
协议要求任何遵循它的类型都必须实现一个 draw
方法。Identifiable
协议要求任何遵循它的类型都必须有一个 id
属性。
遵循协议 #
类、结构体或枚举遵循协议时,需要在类型声明中使用 :协议名称
来表明遵循某个协议,并实现协议中定义的所有需求。
struct Circle: Drawable {
func draw() {
print("Drawing a circle")
}
}
class User: Identifiable {
var id: String
init(id: String) {
self.id = id
}
}
在这个例子中,Circle
结构体遵循了 Drawable
协议,实现了 draw
方法。User
类遵循了 Identifiable
协议,实现了 id
属性。
多个协议 #
Swift 允许一个类型同时遵循多个协议,以逗号分隔。
struct UserProfile: Identifiable, Drawable {
var id: String
func draw() {
print("Drawing user profile with id: \(id)")
}
}
协议的属性要求 #
协议可以要求属性具有特定的 getter 和 setter。如果属性是只读的,只需要指定 { get }
;如果属性是可读写的,则需要指定 { get set }
。
protocol FullyNamed {
var fullName: String { get }
}
protocol Person {
var name: String { get set }
var age: Int { get set }
}
协议中的 Mutating 方法 #
当协议中的方法可能改变类型的实例时,需要在方法前加上 mutating
关键字,通常用于结构体和枚举。
protocol Toggleable {
mutating func toggle()
}
enum LightSwitch: Toggleable {
case off, on
mutating func toggle() {
switch self {
case .off:
self = .on
case .on:
self = .off
}
}
}
协议的继承 #
协议能继承一个或多个其他协议,要求实现多个协议的需求。
protocol Named {
var name: String { get }
}
protocol Aged {
var age: Int { get }
}
protocol Person: Named, Aged { }
Person
协议继承了 Named
和 Aged
协议,遵循 Person
协议的类型需要同时实现 Named
和 Aged
的需求。
协议组合 #
协议组合允许你将多个协议组合成一个单一的要求,使用特殊的 &
语法。
func describe<T: Named & Aged>(person: T) {
print("\(person.name) is \(person.age) years old.")
}
协议扩展 #
协议扩展允许你为协议添加默认实现,使得遵循协议的类型可以仅实现一些而非全部的需求。
protocol Greetable {
func greet()
}
extension Greetable {
func greet() {
print("Hello!")
}
}
struct Person: Greetable {}
let person = Person()
person.greet() // 输出: Hello!
关联类型 #
协议可以包含关联类型要求,通过 associatedtype
关键字定义,用于在协议中使用泛型。
protocol Container {
associatedtype Item
var count: Int { get }
subscript(i: Int) -> Item { get }
}
struct Stack<Element>: Container {
var items: [Element] = []
var count: Int {
return items.count
}
subscript(i: Int) -> Element {
return items[i]
}
}
在这个例子中,Container
协议包含一个关联类型 Item
,Stack
结构体遵循了 Container
协议,并将 Item
关联类型具体化为 Element
。
小结 #
- 定义协议:使用
protocol
关键字定义包含方法、属性、下标等需求的协议。 - 遵循协议:类型通过在声明中使用
:协议名称
表明遵循协议,并实现所有的需求。 - 多个协议:类型可以同时遵循多个协议。
- 属性要求:协议可以要求属性具有特定的 getter 和 setter。
- Mutating 方法:用于需要改变自身实例的方法,通常用于结构体和枚举。
- 协议继承:协议可以继承一个或多个其他协议。
- 协议组合:使用
&
将多个协议组合成一个单一要求。 - 协议扩展:为协议提供默认实现。
- 关联类型:使用
associatedtype
在协议中定义泛型。
协议是 Swift 强大而灵活的类型系统的一部分,广泛用于定义接口并实现多态性、灵活扩展和接口分离。