Swift中关于协议的使用总结

关于Swift协议的基础知识和高级内容,包括:

  1. 协议的基本语法和定义
  2. 协议的遵循
  3. 协议中添加属性
  4. 协议中定义方法(实例方法、类方法、异变方法)
  5. 协议中定义初始化方法
  6. 协议扩展
  7. 与OC的交互
  8. 协议的方法调度机制
  9. 协议的内存结构和底层实现

涵盖:

  • 协议作为类型使用的各种方式
  • 协议继承
  • 协议组合
  • 协议作为泛型约束
  • 协议与扩展的结合使用
  • 协议的关联类型
  • Self 类型
  • 协议的一致性检查
  • 协议用于实现设计模式
  • 常见的Swift标准库中的协议实例

协议是Swift中极其强大且灵活的特性,它可以定义方法、属性和其他特定任务或功能的蓝图。以下是Swift协议的全面应用场景和详细介绍。

一、协议的基本用法

1. 定义协议

协议定义了一个蓝图,声明遵循者必须实现的属性和方法。

swift 复制代码
protocol Vehicle {
    // 属性要求
    var numberOfWheels: Int { get }
    var description: String { get }
    
    // 方法要求
    func makeNoise()
}

2. 遵循协议

类、结构体和枚举都可以遵循协议:

swift 复制代码
// 结构体遵循协议
struct Bicycle: Vehicle {
    var numberOfWheels: Int = 2
    var description: String {
        return "一辆有\(numberOfWheels)个轮子的自行车"
    }
    
    func makeNoise() {
        print("叮叮叮!")
    }
}

// 类遵循协议
class Car: Vehicle {
    var numberOfWheels: Int = 4
    var description: String {
        return "一辆有\(numberOfWheels)个轮子的汽车"
    }
    
    func makeNoise() {
        print("喇叭声!")
    }
}

// 枚举遵循协议
enum TransportOption: Vehicle {
    case airplane, helicopter, boat
    
    var numberOfWheels: Int {
        switch self {
        case .airplane: return 3
        case .helicopter: return 0
        case .boat: return 0
        }
    }
    
    var description: String {
        switch self {
        case .airplane: return "飞机"
        case .helicopter: return "直升机"
        case .boat: return "船"
        }
    }
    
    func makeNoise() {
        switch self {
        case .airplane: print("轰轰轰!")
        case .helicopter: print("噗噗噗!")
        case .boat: print("隆隆隆!")
        }
    }
}

二、协议作为类型

1. 协议类型的变量和常量

协议可以像普通类型一样使用:

swift 复制代码
let bicycle: Vehicle = Bicycle()
let car: Vehicle = Car()
let transport: Vehicle = TransportOption.boat

// 使用协议类型的数组
let vehicles: [Vehicle] = [bicycle, car, transport]
for vehicle in vehicles {
    print("描述: \(vehicle.description)")
    vehicle.makeNoise()
}

2. 协议类型作为函数参数

swift 复制代码
func printVehicleDetails(vehicle: Vehicle) {
    print("这是\(vehicle.description),有\(vehicle.numberOfWheels)个轮子")
    vehicle.makeNoise()
}

printVehicleDetails(vehicle: bicycle)

3. 协议类型作为返回值

swift 复制代码
func createRandomVehicle() -> Vehicle {
    let random = Int.random(in: 0...2)
    switch random {
    case 0: return Bicycle()
    case 1: return Car()
    default: return TransportOption.airplane
    }
}

let randomVehicle = createRandomVehicle()

三、协议的属性要求

1. 可读属性和可读写属性

swift 复制代码
protocol ReadableProtocol {
    var readableProperty: String { get } // 只读属性
}

protocol WritableProtocol {
    var writableProperty: String { get set } // 可读写属性
}

struct MyStruct: ReadableProtocol, WritableProtocol {
    // 只读属性可以用计算属性或存储属性实现
    var readableProperty: String {
        return "只能读取"
    }
    
    // 可读写属性必须是变量存储属性或get/set计算属性
    var writableProperty: String = "可以读取和修改"
}

2. 类型属性要求

swift 复制代码
protocol ClassProtocol {
    static var typeProperty: Int { get set }
    static func typeMethod()
}

class MyClass: ClassProtocol {
    static var typeProperty: Int = 10
    
    static func typeMethod() {
        print("类型方法被调用")
    }
}

四、协议的方法要求

1. 实例方法

swift 复制代码
protocol TaskProtocol {
    func executeTask()
    func calculateTime() -> TimeInterval
}

class Task: TaskProtocol {
    func executeTask() {
        print("执行任务")
    }
    
    func calculateTime() -> TimeInterval {
        return 2.5
    }
}

2. 异变方法(mutating)

允许在值类型(结构体/枚举)中修改实例:

swift 复制代码
protocol Togglable {
    mutating func toggle()
}

struct Switch: Togglable {
    var isOn: Bool = false
    
    mutating func toggle() {
        isOn = !isOn
    }
}

enum Theme: Togglable {
    case light, dark
    
    mutating func toggle() {
        switch self {
        case .light: self = .dark
        case .dark: self = .light
        }
    }
}

3. 带默认参数的方法

协议方法定义中不能有默认参数值,但在实现时可以添加:

swift 复制代码
protocol Greetable {
    func greet(name: String)
}

struct Person: Greetable {
    // 实现时可以提供默认参数
    func greet(name: String = "朋友") {
        print("你好,\(name)!")
    }
}

let person = Person()
person.greet() // 使用默认参数: "你好,朋友!"
person.greet(name: "小明") // "你好,小明!"

五、协议的初始化器要求

1. 基本初始化器

swift 复制代码
protocol Initializable {
    init(value: Int)
}

class InitializableClass: Initializable {
    var value: Int
    
    // 遵循协议的类必须使用required关键字
    required init(value: Int) {
        self.value = value
    }
}

struct InitializableStruct: Initializable {
    var value: Int
    
    // 结构体不需要required关键字
    init(value: Int) {
        self.value = value
    }
}

2. 带继承的初始化器

swift 复制代码
class SuperClass {
    init() {
        print("SuperClass初始化")
    }
}

class SubClass: SuperClass, Initializable {
    var storedValue: Int
    
    // 需要同时标记required和override
    required override init(value: Int) {
        self.storedValue = value
        super.init()
    }
}

3. 可失败初始化器

swift 复制代码
protocol SafeInitializable {
    init?(safeValue: String)
}

struct SafeNumber: SafeInitializable {
    var number: Int
    
    init?(safeValue: String) {
        guard let number = Int(safeValue) else {
            return nil
        }
        self.number = number
    }
}

let validNumber = SafeNumber(safeValue: "123") // 成功初始化
let invalidNumber = SafeNumber(safeValue: "abc") // 返回nil

六、协议的继承

协议可以继承一个或多个其他协议:

swift 复制代码
protocol Named {
    var name: String { get }
}

protocol Aged {
    var age: Int { get }
}

// PersonProtocol继承了Named和Aged协议
protocol PersonProtocol: Named, Aged {
    var email: String { get set }
}

struct Employee: PersonProtocol {
    // 实现Named协议要求
    var name: String
    
    // 实现Aged协议要求
    var age: Int
    
    // 实现PersonProtocol要求
    var email: String
}

七、协议组合

1. 使用&运算符组合多个协议

swift 复制代码
func wishHappyBirthday(to celebrator: Named & Aged) {
    print("祝\(celebrator.name),\(celebrator.age)岁生日快乐!")
}

let john = Employee(name: "John", age: 30, email: "[email protected]")
wishHappyBirthday(to: john)

2. 组合协议与类要求

swift 复制代码
class Person {
    var firstName: String
    var lastName: String
    
    init(firstName: String, lastName: String) {
        self.firstName = firstName
        self.lastName = lastName
    }
}

protocol FullyNamed {
    var fullName: String { get }
}

// 要求同时为Person类型并遵循FullyNamed协议
func printFullNameOfPerson(person: Person & FullyNamed) {
    print("全名: \(person.fullName)")
}

class Student: Person, FullyNamed {
    var grade: Int
    
    init(firstName: String, lastName: String, grade: Int) {
        self.grade = grade
        super.init(firstName: firstName, lastName: lastName)
    }
    
    var fullName: String {
        return "\(firstName) \(lastName)"
    }
}

let student = Student(firstName: "李", lastName: "明", grade: 3)
printFullNameOfPerson(person: student)

八、协议扩展

1. 为协议添加默认实现

swift 复制代码
protocol TextRepresentable {
    var textDescription: String { get }
}

// 提供默认实现
extension TextRepresentable {
    var textDescription: String {
        return "这是一个TextRepresentable类型"
    }
}

// 可以使用默认实现
struct SimpleStruct: TextRepresentable {
    // 不需要实现textDescription,将使用默认实现
}

// 可以提供自定义实现
struct DetailedStruct: TextRepresentable {
    var textDescription: String {
        return "这是一个详细描述"
    }
}

let simple = SimpleStruct()
print(simple.textDescription) // "这是一个TextRepresentable类型"

let detailed = DetailedStruct()
print(detailed.textDescription) // "这是一个详细描述"

2. 有条件的协议扩展

可以只对满足特定条件的类型提供扩展:

swift 复制代码
// 只为Collection协议扩展,且其元素是TextRepresentable的
extension Collection where Element: TextRepresentable {
    var allTextDescriptions: String {
        return self.map { $0.textDescription }.joined(separator: ", ")
    }
}

let textItems: [TextRepresentable] = [SimpleStruct(), DetailedStruct()]
print(textItems.allTextDescriptions)

3. 扩展协议添加更多功能

swift 复制代码
protocol Identifiable {
    var id: String { get }
}

extension Identifiable {
    // 添加默认实现
    var id: String {
        return UUID().uuidString
    }
    
    // 添加新方法
    func identify() {
        print("ID: \(id)")
    }
}

struct User: Identifiable {
    // 可选择是否重写id
}

let user = User()
user.identify() // 使用扩展中添加的方法

九、泛型与协议

1. 协议作为泛型约束

swift 复制代码
// T必须遵循Equatable协议
func findIndex<T: Equatable>(of valueToFind: T, in array: [T]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

let numbers = [1, 2, 3, 4, 5]
if let index = findIndex(of: 3, in: numbers) {
    print("找到3在位置: \(index)")
}

2. 关联类型(Associated Types)

协议中可以定义关联类型,这是一个占位名:

swift 复制代码
protocol Container {
    // 定义一个关联类型Item
    associatedtype Item
    
    // 使用关联类型的方法
    mutating func add(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}

// Array实现Container协议
extension Array: Container {
    // Swift自动推断Item为Element类型
    mutating func add(_ item: Element) {
        self.append(item)
    }
    
    // count和subscript已经存在于Array中
}

var numbers = [1, 2, 3]
numbers.add(4)
print(numbers.count) // 4
print(numbers[2]) // 3

3. 约束关联类型

swift 复制代码
protocol SortableContainer: Container where Item: Comparable {
    func sorted() -> [Item]
}

extension Array: SortableContainer where Element: Comparable {
    func sorted() -> [Element] {
        return self.sorted()
    }
}

let names = ["Tom", "Alex", "John"]
let sortedNames = names.sorted() // ["Alex", "John", "Tom"]

十、协议的一致性检查

1. is运算符

检查实例是否遵循特定协议:

swift 复制代码
protocol Flyable {
    func fly()
}

struct Bird: Flyable {
    func fly() {
        print("鸟在飞!")
    }
}

let object: Any = Bird()

if object is Flyable {
    print("这个对象可以飞行")
}

2. as?和as!运算符

尝试将实例转换为特定协议类型:

swift 复制代码
if let flyingObject = object as? Flyable {
    flyingObject.fly() // 调用协议方法
}

十一、使用协议实现设计模式

1. 代理模式

swift 复制代码
// 定义代理协议
protocol TaskDelegate: AnyObject {
    func taskDidStart(_ task: TaskManager)
    func taskDidComplete(_ task: TaskManager)
    func task(_ task: TaskManager, didFailWithError error: Error)
}

class TaskManager {
    weak var delegate: TaskDelegate?
    
    func startTask() {
        delegate?.taskDidStart(self)
        // 任务实现...
        delegate?.taskDidComplete(self)
    }
    
    func handleError(_ error: Error) {
        delegate?.task(self, didFailWithError: error)
    }
}

class TaskViewController: UIViewController, TaskDelegate {
    let taskManager = TaskManager()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        taskManager.delegate = self
    }
    
    func startOperation() {
        taskManager.startTask()
    }
    
    // TaskDelegate方法实现
    func taskDidStart(_ task: TaskManager) {
        print("任务开始")
    }
    
    func taskDidComplete(_ task: TaskManager) {
        print("任务完成")
    }
    
    func task(_ task: TaskManager, didFailWithError error: Error) {
        print("任务失败: \(error.localizedDescription)")
    }
}

2. 策略模式

swift 复制代码
protocol SortingStrategy {
    func sort<T: Comparable>(_ items: [T]) -> [T]
}

struct QuickSort: SortingStrategy {
    func sort<T: Comparable>(_ items: [T]) -> [T] {
        // 实现快速排序...
        return items.sorted()
    }
}

struct MergeSort: SortingStrategy {
    func sort<T: Comparable>(_ items: [T]) -> [T] {
        // 实现归并排序...
        return items.sorted()
    }
}

class SortContext {
    private var strategy: SortingStrategy
    
    init(strategy: SortingStrategy) {
        self.strategy = strategy
    }
    
    func setStrategy(_ strategy: SortingStrategy) {
        self.strategy = strategy
    }
    
    func executeSort<T: Comparable>(_ items: [T]) -> [T] {
        return strategy.sort(items)
    }
}

// 使用
let sortContext = SortContext(strategy: QuickSort())
var result = sortContext.executeSort([3, 1, 4, 1, 5, 9])

// 切换策略
sortContext.setStrategy(MergeSort())
result = sortContext.executeSort([3, 1, 4, 1, 5, 9])

十二、Self要求

1. 使用Self表示遵循者类型

swift 复制代码
protocol Copyable {
    // Self代表遵循此协议的具体类型
    func copy() -> Self
}

struct Point: Copyable {
    var x: Int
    var y: Int
    
    func copy() -> Self {
        return Point(x: self.x, y: self.y)
    }
}

class Document: Copyable {
    var title: String
    var content: String
    
    init(title: String, content: String) {
        self.title = title
        self.content = content
    }
    
    func copy() -> Self {
        // 需要创建正确的Self类型,这里用了类型转换
        let copy = type(of: self).init(title: title, content: content)
        return copy
    }
    
    // 为了支持上面的init方法,需要添加required初始化器
    required init(title: String, content: String) {
        self.title = title
        self.content = content
    }
}

2. 静态成员中的Self

swift 复制代码
protocol Factory {
    static func create() -> Self
}

struct ProductA: Factory {
    static func create() -> Self {
        return ProductA()
    }
}

let product = ProductA.create()

十三、与Objective-C交互的协议

1. @objc协议

swift 复制代码
// 只能被类采纳的协议
@objc protocol DataSourceDelegate {
    // 必须实现的方法
    func numberOfItems() -> Int
    
    // 可选实现的方法
    @objc optional func itemAt(index: Int) -> Any?
}

class DataProvider: NSObject, DataSourceDelegate {
    func numberOfItems() -> Int {
        return 10
    }
    
    // 可选方法,不是必须实现的
}

let provider = DataProvider()
print(provider.numberOfItems()) // 10

2. 继承NSObjectProtocol

swift 复制代码
class CustomView: UIView, NSObjectProtocol {
    // NSObjectProtocol方法已通过UIView继承的NSObject实现
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
}

十四、协议的性能优化

1. 协议默认实现中的static分派

swift 复制代码
protocol Drawable {
    func draw()
}

extension Drawable {
    func draw() {
        print("默认绘制实现")
    }
    
    // 添加未在协议中声明的方法使用静态分派
    func prepare() {
        print("准备绘制")
    }
}

struct Canvas: Drawable {
    // 使用自定义实现代替默认实现
    func draw() {
        print("Canvas绘制实现")
    }
    
    // 重写未在协议中声明的方法
    func prepare() {
        print("Canvas准备")
    }
}

let shape: Drawable = Canvas()
shape.draw() // "Canvas绘制实现" - 动态分派
shape.prepare() // "准备绘制" - 静态分派,而不是"Canvas准备"

2. 使用final关键字优化

swift 复制代码
final class OptimizedDrawing: Drawable {
    func draw() {
        print("优化的绘制")
    }
}

十五、协议的内存模型

如前面文章中所述,Swift协议在内存上通过存在容器(Existential Container)和协议见证表(Protocol Witness Table)实现,具体分为:

  1. 小值类型直接存储在容器的Value Buffer中
  2. 大值类型在堆上分配内存,采用写时复制优化
  3. 引用类型存储实例的堆地址
swift 复制代码
// 1. 小值类型示例
protocol SmallValueProtocol {
    var value: Int { get }
}

struct SmallValue: SmallValueProtocol {
    var value: Int
}

// 2. 大值类型示例
protocol LargeValueProtocol {
    var values: [Int] { get }
}

struct LargeValue: LargeValueProtocol {
    var values: [Int] // 数组在堆上管理
}

// 3. 引用类型示例
protocol ReferenceProtocol {
    var id: Int { get }
}

class Reference: ReferenceProtocol {
    var id: Int
    
    init(id: Int) {
        self.id = id
    }
}

十六、常见的标准库协议

1. Equatable

swift 复制代码
struct Point: Equatable {
    var x: Int
    var y: Int
    
    // 自动合成的==运算符实现
    // static func == (lhs: Point, rhs: Point) -> Bool {
    //     return lhs.x == rhs.x && lhs.y == rhs.y
    // }
}

let p1 = Point(x: 1, y: 2)
let p2 = Point(x: 1, y: 2)
print(p1 == p2) // true

2. Comparable

swift 复制代码
struct Person: Comparable {
    var name: String
    var age: Int
    
    static func < (lhs: Person, rhs: Person) -> Bool {
        return lhs.age < rhs.age
    }
    
    static func == (lhs: Person, rhs: Person) -> Bool {
        return lhs.age == rhs.age
    }
}

let people = [
    Person(name: "张三", age: 30),
    Person(name: "李四", age: 25),
    Person(name: "王五", age: 40)
]

let sortedPeople = people.sorted()
// 排序后: 李四(25), 张三(30), 王五(40)

3. Hashable

swift 复制代码
struct Product: Hashable {
    var id: Int
    var name: String
    
    // 自动合成hash(into:)方法
    // func hash(into hasher: inout Hasher) {
    //     hasher.combine(id)
    //     hasher.combine(name)
    // }
}

let products = [
    Product(id: 1, name: "iPhone"),
    Product(id: 2, name: "iPad"),
    Product(id: 3, name: "MacBook")
]

let productSet = Set(products)
let productDict = Dictionary(uniqueKeysWithValues: products.map { ($0, $0.id) })

4. Codable (Encodable & Decodable)

swift 复制代码
struct User: Codable {
    var id: Int
    var name: String
    var email: String
}

// 编码
let user = User(id: 1, name: "张三", age: "[email protected]")
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted

do {
    let data = try encoder.encode(user)
    if let json = String(data: data, encoding: .utf8) {
        print(json)
    }
} catch {
    print("编码失败: \(error)")
}

// 解码
let jsonString = """
{
  "id": 2,
  "name": "李四",
  "email": "[email protected]"
}
"""

if let jsonData = jsonString.data(using: .utf8) {
    let decoder = JSONDecoder()
    do {
        let decodedUser = try decoder.decode(User.self, from: jsonData)
        print("解码用户: \(decodedUser.name)")
    } catch {
        print("解码失败: \(error)")
    }
}

5. CustomStringConvertible

swift 复制代码
struct Book: CustomStringConvertible {
    var title: String
    var author: String
    var year: Int
    
    var description: String {
        return "\(title) by \(author) (\(year))"
    }
}

let book = Book(title: "Swift编程", author: "苹果", year: 2023)
print(book) // "Swift编程 by 苹果 (2023)"

十七、总结

Swift的协议是一种极其强大的抽象机制,可以用于:

  1. 定义接口和契约
  2. 实现多态性
  3. 组织代码和分离关注点
  4. 提供默认实现
  5. 实现各种设计模式
  6. 支持泛型编程
  7. 在不同类型间共享行为

通过合理使用协议,可以创建更灵活、可扩展且可复用的代码,这也是Swift语言的核心设计理念之一。

Swift协议有三种方法调度方式:

  • 静态派发(结构体方法、未在协议中声明的扩展方法)
  • 表派发(通过witness_table,协议中声明的方法)
  • 消息派发(使用@objc dynamic标记的方法)

理解这些调度机制有助于编写更高效的Swift代码。

相关推荐
uhakadotcom1 小时前
PostgreSQL 行级安全性(RLS)简介
后端·面试·github
uhakadotcom2 小时前
Tableau入门:数据可视化的强大工具
后端·面试·github
程序员鱼皮3 小时前
2025 年最全Java面试题 ,热门高频200 题+答案汇总!
java·后端·面试
uhakadotcom4 小时前
APM系统简介及案例
后端·面试·github
uhakadotcom4 小时前
Syslog投递日志到SIEM:基础知识与实践
后端·面试·github
uhakadotcom4 小时前
Flume 和 Logstash:日志收集工具的对比
后端·面试·github
uhakadotcom4 小时前
Kibana入门:数据分析和可视化的强大工具
后端·面试·github
池鱼ipou4 小时前
《JavaScript的“套娃陷阱”:90%程序员栽过的三种复制大坑》
前端·javascript·面试
池鱼ipou5 小时前
春招面试拷打实录(三):面试血泪史,面经干货分享!!!
前端·vue.js·面试
Cutey9165 小时前
H5页面嵌入项目的完整方案
前端·javascript·面试