Swift — 协议(Protocol)

在 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 协议继承了 NamedAged 协议,遵循 Person 协议的类型需要同时实现 NamedAged 的需求。

协议组合 #

协议组合允许你将多个协议组合成一个单一的要求,使用特殊的 & 语法。

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 协议包含一个关联类型 ItemStack 结构体遵循了 Container 协议,并将 Item 关联类型具体化为 Element

小结 #

  • 定义协议:使用 protocol 关键字定义包含方法、属性、下标等需求的协议。
  • 遵循协议:类型通过在声明中使用 :协议名称 表明遵循协议,并实现所有的需求。
  • 多个协议:类型可以同时遵循多个协议。
  • 属性要求:协议可以要求属性具有特定的 getter 和 setter。
  • Mutating 方法:用于需要改变自身实例的方法,通常用于结构体和枚举。
  • 协议继承:协议可以继承一个或多个其他协议。
  • 协议组合:使用 & 将多个协议组合成一个单一要求。
  • 协议扩展:为协议提供默认实现。
  • 关联类型:使用 associatedtype 在协议中定义泛型。

协议是 Swift 强大而灵活的类型系统的一部分,广泛用于定义接口并实现多态性、灵活扩展和接口分离。

本文共 1254 字,上次修改于 Dec 16, 2024