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

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

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

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

协议基本实现

协议定义

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

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

swift 复制代码
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{};
  • 定义数据结构
swift 复制代码
struct Person {
    let name: String
    let age: Int
    let yearsOfExperience: Int
}
  • 实现 TabularDataSource 协议
swift 复制代码
//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是另一个系统实义的协议,注意代码写法,这里需要用到 &关键字。

swift 复制代码
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)
    }
}

协议测试

swift 复制代码
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的接口比较相似,并且可以多继承

swift 复制代码
protocol MyProtocol : ProtocolOne, ProtocolTwo{

}

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

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

}

//协议实现
struct Department: TabularDataSource{

}

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

}

协议组合

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

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

}

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

}

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

}
相关推荐
pe7er2 天前
macOS 应用无法打开(权限问题)解决方案
macos·mac
CocoaKier2 天前
苹果谷歌商店:如何监控并维护用户评分评论
ios·google·apple
iOS日常2 天前
iOS设备崩溃日志获取与查看
ios·xcode
wangruofeng2 天前
AI 助力 Flutter 3.27 升级到 3.38 完整指南:两周踩坑与实战复盘
flutter·ios·ai编程
iOS日常3 天前
Xcode 垃圾清理
ios·xcode
开心就好20253 天前
不越狱能抓到 HTTPS 吗?在未越狱 iPhone 上抓取 HTTPS
后端·ios
傅里叶3 天前
iOS相机权限获取
flutter·ios
埃博拉酱3 天前
VS Code Remote SSH 连接 Windows 服务器卡在"下载 VS Code 服务器":prcdn DNS 解析失败的诊断与 BITS 断点续传
windows·ssh·visual studio code
郑州光合科技余经理4 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
feifeigo1234 天前
matlab画图工具
开发语言·matlab