Swift中协议(Protocol)都能做什么?

在 Swift 中,协议(Protocol)是定义方法、属性、下标等“蓝图”的规则集合,核心作用是实现代码抽象、解耦和多态,具体可做以下关键事情:


1. 定义接口规范:仅声明方法、属性的“签名”(如方法名、参数/返回值类型,属性的 getter/setter 权限),不提供具体实现,强制遵循者(类、结构体、枚举)满足规范。

例:定义 Drawable 协议声明 draw() 方法,Circle 和 Square 遵循协议后必须各自实现 draw()。


2. 实现多态:不同类型(如类、结构体)遵循同一协议后,可被统一当作“协议类型”使用,调用协议方法时自动执行具体类型的实现。

例:let shapes: [Drawable] = [Circle(), Square()],遍历 shapes 调用 draw() 时,会分别执行圆形、正方形的绘制逻辑。


3. 实现多重“继承”能力:Swift 不支持类的多继承,但一个类型(类/结构体/枚举)可遵循多个协议,从而整合多个协议的规范(结构体和枚举甚至能同时遵循协议+继承其他值类型)。

例:class Person: Codable, Equatable,Person 同时满足“可编码”和“可比较”的协议要求。


4. 扩展功能(协议扩展):通过 extension 为协议添加默认实现(遵循者可直接使用,无需重复编写)或额外方法,统一扩展所有遵循者的能力。

例:为 Collection 协议(数组、字典等都遵循)扩展 randomElement() 方法,所有集合类型都能直接调用该方法获取随机元素。


5. 作为类型约束:在泛型、函数参数中用协议限制类型范围,确保传入的类型满足特定能力。

例:func printArea<T: Shape>(shape: T),T 必须是遵循 Shape 协议的类型(确保有计算面积的属性/方法),否则无法调用该函数。


对应 Swift 协议的 5 个核心作用案例


1. 作用:定义接口规范(强制遵循者满足统一规则)


核心逻辑:协议仅声明“必须做什么”(如 draw() 方法),不关心“怎么做”,遵循者必须实现具体逻辑。

// 1. 定义协议(接口规范):声明必须实现 draw() 方法

protocol Drawable {

    func draw() // 仅声明方法签名,无实现

}


// 2. 结构体遵循协议,必须实现 draw()

struct Circle: Drawable {

    func draw() { // 具体实现:绘制圆形

        print("绘制一个红色圆形,半径 5cm")

    }

}


struct Square: Drawable {

    func draw() { // 具体实现:绘制正方形

        print("绘制一个蓝色正方形,边长 8cm")

    }

}


// 3. 使用:调用遵循者的方法

let circle = Circle()

let square = Square()

circle.draw() // 输出:绘制一个红色圆形,半径 5cm

square.draw() // 输出:绘制一个蓝色正方形,边长 8cm

关联解释:Drawable 协议强制 Circle 和 Square 必须实现 draw(),确保所有“可绘制”类型都具备“绘制”能力,统一了接口规范。


2. 作用:实现多态(统一协议类型,自动执行具体实现)


核心逻辑:用“协议类型”存储不同遵循者的实例,调用协议方法时,自动匹配实例的具体实现。

// 基于上面已定义的 Drawable 协议、Circle、Square

func testPolymorphism() {

    // 1. 定义“协议类型数组”:存储所有遵循 Drawable 的实例

    let drawables: [Drawable] = [Circle(), Square(), Circle()]

   

    // 2. 遍历数组,统一调用 draw() 方法

    for item in drawables {

        item.draw() // 无需判断类型,自动执行实例的具体实现

    }

}


// 调用函数,查看多态效果

testPolymorphism()

// 输出:

// 绘制一个红色圆形,半径 5cm

// 绘制一个蓝色正方形,边长 8cm

// 绘制一个红色圆形,半径 5cm

关联解释:数组 drawables 的类型是 [Drawable](协议类型),但存储了 Circle 和 Square 实例。遍历调用 draw() 时,无需区分类型,自动执行对应实例的实现,这就是多态的核心价值。


3. 作用:多重“继承”能力(整合多个协议的规范)


核心逻辑:Swift 类不支持多继承,但可同时遵循多个协议,整合多个协议的能力(如“可编码”+“可比较”)。

// 1. 直接使用系统内置协议 Codable(可编码/解码)和 Equatable(可比较)

class Person: Codable, Equatable {

    let name: String

    let age: Int

   

    // 2. 初始化方法

    init(name: String, age: Int) {

        self.name = name

        self.age = age

    }

   

    // 3. 遵循 Equatable 必须实现 == 方法(比较逻辑)

    static func == (lhs: Person, rhs: Person) -> Bool {

        return lhs.name == rhs.name && lhs.age == rhs.age

    }

}


// 4. 测试多重协议能力

// 能力1:Equatable 的“比较”能力

let person1 = Person(name: "张三", age: 25)

let person2 = Person(name: "张三", age: 25)

let person3 = Person(name: "李四", age: 30)

print(person1 == person2) // 输出:true(满足 Equatable 规范)

print(person1 == person3) // 输出:false


// 能力2:Codable 的“JSON 编码”能力

let encoder = JSONEncoder()

encoder.outputFormatting = .prettyPrinted

if let jsonData = try? encoder.encode(person1),

  let jsonStr = String(data: jsonData, encoding: .utf8) {

    print(jsonStr) // 输出 Person 的 JSON 字符串(满足 Codable 规范)

}

关联解释:Person 同时遵循 Codable 和 Equatable,既具备“JSON 编码/解码”能力,又具备“实例比较”能力,相当于整合了两个协议的规范,实现了类似“多继承”的效果。


4. 作用:协议扩展(添加默认实现/统一扩展能力)


核心逻辑:通过 extension 为协议添加默认方法,所有遵循者可直接使用,无需重复编写代码。

// 1. 定义协议:声明“计算总长度”的能力

protocol HasTotalLength {

    func calculateTotalLength() -> Int // 协议声明的方法

}


// 2. 协议扩展:为所有遵循者添加默认实现

extension HasTotalLength where Self: Collection, Element == String {

    // 默认实现:计算所有字符串的总长度

    func calculateTotalLength() -> Int {

        return self.reduce(0) { $0 + $1.count }

    }

   

    // 额外添加协议未声明的方法(统一扩展能力)

    func printEachElementLength() {

        for (index, str) in self.enumerated() {

            print("第\(index+1)个元素长度:\(str.count)")

        }

    }

}


// 3. 数组遵循协议(无需手动实现 calculateTotalLength,直接用默认实现)

let stringArray: [String] = ["Swift", "Protocol", "Extension"]

let totalLength = stringArray.calculateTotalLength()

print("总长度:\(totalLength)") // 输出:总长度:19(5+8+6)


// 调用扩展添加的额外方法

stringArray.printEachElementLength()

// 输出:

// 第1个元素长度:5

// 第2个元素长度:8

// 第3个元素长度:6

关联解释:HasTotalLength 协议通过扩展,为所有“元素是 String 的 Collection 类型”(如数组、Set)提供了 calculateTotalLength() 的默认实现和 printEachElementLength() 的额外方法。stringArray 无需手动实现协议方法,直接调用即可,实现了“一次扩展,所有遵循者复用”。


5. 作用:作为类型约束(限制泛型/参数的类型范围)


核心逻辑:在泛型中用协议约束类型,确保传入的类型具备特定能力(如“能计算面积”)。

// 1. 定义协议:声明“能计算面积”的能力

protocol Shape {

    var area: Double { get } // 只读属性:面积

}


// 2. 遵循协议的类型

struct CircleShape: Shape {

    let radius: Double

    // 实现 area 属性:圆面积 = πr²

    var area: Double {

        return Double.pi * radius * radius

    }

}


struct SquareShape: Shape {

    let sideLength: Double

    // 实现 area 属性:正方形面积 = 边长²

    var area: Double {

        return sideLength * sideLength

    }

}


// 3. 泛型函数:用 Shape 协议约束类型 T

// 约束:T 必须是遵循 Shape 的类型(确保有 area 属性)

func printShapeArea<T: Shape>(shape: T, shapeName: String) {

    print("\(shapeName)的面积:\(shape.area)(保留2位小数)")

}


// 4. 调用函数:仅能传入遵循 Shape 的类型

let circle = CircleShape(radius: 3)

let square = SquareShape(sideLength: 4)


printShapeArea(shape: circle, shapeName: "圆形") // 输出:圆形的面积:28.27(保留2位小数)

printShapeArea(shape: square, shapeName: "正方形") // 输出:正方形的面积:16.0(保留2位小数)


// 错误示例:传入非 Shape 类型(如 Int)会编译失败

// let num = 10

// printShapeArea(shape: num, shapeName: "数字") // 编译报错:Int 不遵循 Shape

关联解释:泛型函数 printShapeArea 通过 <T: Shape> 约束 T 必须是 Shape 协议的遵循者,确保传入的 shape 一定有 area 属性,避免了“传入无法计算面积的类型”的错误,保证了函数逻辑的安全性。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容