Swift-29-高级编程-协议详解

在学习了利用访问控制来隐藏信息。隐藏信息是封装(encapsulation)的一种形式,可以在设计软件时达到其中一部分的变化不影响其他部分的目的。Swift还提供另一种形式的封装:协议。协议可以让你无须知道类型本身的信息,就能指定并利用类型的接口(interface),接口是类型提供的一组属性和方法。

Swift中的协议和ObjectiveC中的协议的概念是一样的,主要目的是在不修改类结构的前提下扩展类的功能。一个类可以实现多个协议。但在实现方式上Swift中的协议更像java接口的概念。

本节会以一个复杂的例子来学习协议的基本内容,我们会在这个例子中打印下列这样的一个表格。
在这里插入图片描述

协议基本实现

协议定义

语法结构: protoclo proName{ properties..., function...}

用protocol关键字定义,可以定义属性和方法,首先定义一个名为TabularDataSource的协议。

protocol TabularDataSource {
    //定义协议属性和必须要实现的属性读取方法,get表示属性可读,也可以设置成{get set}
    var numberOfRows: Int { get }
    var numberOfColumns: Int { get }

    //定义协议接口方法,所有实现此协议的类必须要实现以下两个方法
    func label(forColumn column: Int) -> String //行标签
    func itemFor(row: Int, column: Int) -> String //单元格数据
}

上述定义有一点需要注意,因为协议可由类和结构体实现,但结构体中的方法默认是不能改改其self值的,如果需要修改,则可将协议的 func label(){} 函数定义修改为 mutaling func label(){}。

协议实现

语法格式:

  1. 结构体和类实现语法:struct strName:ProtocolName,在结构体名称后面加上冒号+协议名称,比如struct Department: TabularDataSource;
  2. 类实现语法定义:类分有父类和无父类两种情况,写法一样,由swift自已来识别,比如
    • 无父类:class className:protocolName1, protocolName2{};
    • 有父类:class className:SuperClass, protocolName1, protocolName2{};
  • 定义数据结构
struct Person {
    let name: String
    let age: Int
    let yearsOfExperience: Int
}
  • 实现 TabularDataSource 协议
//CustomStringConvertible是框架定义的一个协议,它里面只定义了一个名为description的属性,相当于toString方法
struct Department: TabularDataSource, CustomStringConvertible {
    let name: String
    var people = [Person]()

    var description: String {
        return "Department (\(name))"
    }

    init(name: String) {
        self.name = name
    }

    mutating func add(_ person: Person) {
        people.append(person)
    }

   //协议属性,必须实现
    var numberOfRows: Int {
        return people.count
    }

    var numberOfColumns: Int {
        return 3
    }

   //协议接口方法,必须实现
    func label(forColumn column: Int) -> String {
        switch column {
        case 0: return "Employee Name"
        case 1: return "Age"
        case 2: return "Years of Experience"
        default: fatalError("Invalid column!")
        }
    }

    //协议接口方法,必须实现
    func itemFor(row: Int, column: Int) -> String {
        let person = people[row]
        switch column {
        case 0: return person.name
        case 1: return String(person.age)
        case 2: return String(person.yearsOfExperience)
        default: fatalError("Invalid column!")
        }
    }
}

协议做为参数使用

把协议做为参数传递,CustomStringConvertible是另一个系统实义的协议,注意代码写法,这里需要用到 &关键字。

func printTable(_ dataSource: TabularDataSource & CustomStringConvertible) {
    print("Table: \(dataSource.description)")

    // 创建表格头
    var firstRow = "|"

    // 记录每一列的宽度
    var columnWidths = [Int]()

    for i in 0 ..< dataSource.numberOfColumns {
        let columnLabel = dataSource.label(forColumn: i)
        let columnHeader = " \(columnLabel) |"
        firstRow += columnHeader
        columnWidths.append(columnLabel.count)
    }

    print(firstRow)

    //拼装行数据
    for i in 0 ..< dataSource.numberOfRows {
        // 创建一个空符串
        var out = "|"

        // 拼接第一列数据到一行上
        for j in 0 ..< dataSource.numberOfColumns {
            let item = dataSource.itemFor(row: i, column: j)
            let paddingNeeded = columnWidths[j] - item.count
            let padding = repeatElement(" ", count: paddingNeeded).joined(separator: "")
            out += " \(padding)\(item) |"
        }

        print(out)
    }
}

协议测试

var department = Department(name: "Engineering")
department.add(Person(name: "Joe", age: 30, yearsOfExperience: 6))
department.add(Person(name: "Karen", age: 40, yearsOfExperience: 18))
department.add(Person(name: "Fred", age: 50, yearsOfExperience: 20))

printTable(department)

在这里插入图片描述

高级用法

协议继承

语法结构:协议支持协议的继承,这点和java的接口比较相似,并且可以多继承

protocol MyProtocol : ProtocolOne, ProtocolTwo{

}

这样上一节小的例子就可以定义成下列的结构,代码如下:

//定义协议-继承模式, CustomStringConvertible为系统提供的一个标准协议
protocol TabularDataSource: CustomStringConvertible {

}

//协议实现
struct Department: TabularDataSource{

}

//协议做为参数使用
func printTable(_ dataSource: TabularDataSource){

}

协议组合

对于上面继承的模式实现有时不是太建议,因为灵活性差一些。因为这样架构的话会把两个不相关的功能耦合在了一起。所以一般很少用到协议继承。所以最好用组合的方式改成如下代码实现。

  • 两个协议组合可以用&来连接,比如:TabularDataSource, CustomStringConvertible
  • 多个协议组合可用用<>来连接,比如:<TabularDataSource, CustomStringConvertible, threeAble>
//协议定义
protocol TabularDataSource{

}

//协议实现
struct Department: TabularDataSource, CustomStringConvertible{

}

//协议做为参数使用-组合模式
func printTable(_ dataSource: TabularDataSource & CustomStringConvertible) {

}

相关推荐

  1. Swift 协议

    2024-04-25 06:38:07       25 阅读
  2. Swift网络编程

    2024-04-25 06:38:07       41 阅读
  3. Swift 基于Codable协议使用

    2024-04-25 06:38:07       23 阅读
  4. 【Python高级编程】使用OpenCV进行图像旋转详解

    2024-04-25 06:38:07       32 阅读
  5. [Swift]RxSwift常见用法详解

    2024-04-25 06:38:07       54 阅读
  6. Swift - swiftc

    2024-04-25 06:38:07       30 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-25 06:38:07       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-25 06:38:07       106 阅读
  3. 在Django里面运行非项目文件

    2024-04-25 06:38:07       87 阅读
  4. Python语言-面向对象

    2024-04-25 06:38:07       96 阅读

热门阅读

  1. 前端开发需掌握的linux命令行

    2024-04-25 06:38:07       37 阅读
  2. SpringBoot钩子函数

    2024-04-25 06:38:07       32 阅读
  3. C# ToString

    2024-04-25 06:38:07       35 阅读
  4. 2024-04-24 问AI: 在深度学习中,CUDA 是什么?

    2024-04-25 06:38:07       38 阅读
  5. 【SAP ME 27】SAP ME创建开发组件(DC)serviceExt

    2024-04-25 06:38:07       37 阅读
  6. vue - 路由守卫

    2024-04-25 06:38:07       32 阅读