• Swift 协议


    协议的语法

    • 自定义类型声明时,将协议名放在类型名的冒号之后来表示该类型采纳一个特定的协议。多个协议可以用逗号分开列出。
    • 若一个类拥有父类,将这个父类名放在其采纳的协议名之前,并用逗号分隔。
    1. protocol SomeProtocol {
    2.     //定义了一个协议
    3. }
    4. protocol AnotherProtocol {
    5.     //定义了一个协议
    6. }
    7. protocol OneProtocol: SomeProtocol, AnotherProtocol {
    8.     //定义了一个协议 OneProtocol, 他分别遵循 SomeProtocol 和 AnotherProtocol 协议
    9. }
    10. class SomeSuperClass {
    11.     //定义了一个基类
    12. }
    13. class SomeClas: SomeSuperClass, SomeProtocol, AnotherProtocol {
    14.     //定义了一个类 SomeClas, 他有一个父类 SomeSuperClass, 并且他遵循了 SomeProtocol 和 AnotherProtocol 协议
    15. }


    属性要求

    • 协议可以要求所有遵循该协议的类型提供特定名字和类型的实例属性或类型属性。协议并不会具体说明属性是储存型属性还是计算型属性——它只具体要求属性有特定的名称和类型。协议同时要求一个属性必须明确是可读的或可读的和可写的。
    • 若协议要求一个属性为可读和可写的,那么该属性要求不能用常量存储属性或只读计算属性来满足。若协议只要求属性为可读的,那么任何种类的属性都能满足这个要求,而且如果你的代码需要的话,该属性也可以是可写的。
    1. protocol SomeProtocol {
    2.     var mustBeSettable: Int { get set }  //协议要求属性必须是可写的
    3.     var doesNotNeedTobeSettable: Int { get }   //协议要求属性可以不是可写的
    4. }
    1. protocol FullyNamed {
    2.     var fullName: String { get }
    3. }
    4. struct Person: FullyNamed  {
    5.     var fullName: String
    6. }
    7. let john = Person(fullName: "John")
    8. class StarShip: FullyNamed {
    9.     var prefix: String?
    10.     var name: String
    11.     init(name: String, prefix: String? = nil) {
    12.         self.name = name
    13.         self.prefix = prefix
    14.     }
    15.     var fullName: String {  // StarShip 类遵循了 FullyNamed 协议,必须要实现协议中属性的可读属性
    16.         return (prefix != nil ? prefix! + " " : "") + name
    17.     }
    18. }
    19. var sta = StarShip(name: "EnterPrice", prefix: "USS")
    20. print(sta.name)
    21. print(sta.fullName)
    22. print(sta.prefix ?? "")

    • 在协议中定义类型属性时在前面添加 static 关键字。当类的实现使用 class 或 static 关键字前缀声明类型属性要求时,这个规则仍然适用。
    1. protocol SomeProtocol {
    2.     //在协议中定义类型属性时在前面添加 static 关键字
    3.     static var someTypeProperty: Int { get }
    4. }
    5. class someClass: SomeProtocol {
    6.     static var someTypeProperty: Int {
    7.         return 3
    8.     }
    9. }

    方法要求

    • 协议可以要求采纳的类型实现指定的实例方法和类方法。这些方法作为协议定义的一部分,书写方式与正常实例和类方法的方式完全相同,但是不需要大括号和方法的主体。允许变量拥有参数,与正常的方法使用同样的规则。但在协议的定义中,方法参数不能定义默认值。
    • 正如类型属性要求的那样,当协议中定义类型方法时,你总要在其之前添加 static 关键字。即使在类 实现时,类型方法要求使用 class 或 static 作为关键字前缀,前面的规则仍然适用。

    mutating 方法要求

    • 若你定义了一个协议的实例方法需求,想要异变任何采用了该协议的类型实例,只需在协议里 方法的定义当中使用 mutating 关键字。这允许结构体和枚举类型能采用相应协议并满足方法 要求。
    1. //枚举是值类型,在值类型的实例方法中,将mutating关键字作为函数的前缀,写在func之前,表示可以在该方法中修改它所属的实例及其实例属性的值。
    2. enum Directions: Direction {
    3.     case north, south, east, west
    4.     mutating func show() {
    5.         switch self {
    6.         case .north:
    7.             self = .north
    8.             print("north")
    9.         case .south:
    10.             self = .south
    11.             print("south")
    12.         case .east:
    13.             self = .east
    14.             print("east")
    15.         default:
    16.             self = .west
    17.             print("west")
    18.         }
    19.     }
    20. }
    21. var dir = Directions.east
    22. print(dir)


    初始化器要求

    • 协议可以要求遵循协议的类型实现指定的初始化器。和一般的初始化器一样,只用将初始化器写在协议的定义当中,只是不用写大括号也就是初始化器的实体。

    初始化器要求的类实现

    • 你可以通过实现指定初始化器或便捷初始化器来使遵循该协议的类满足协议的初始化器要求。 在这两种情况下,你都必须使用 required 关键字修饰初始化器的实现。

    • 如果一个子类重写了父类指定的初始化器,并且遵循协议实现了初始化器要求,那么就要为这 个初始化器的实现添加 required 和 override 两个修饰符。
    1. protocol SomeProtocol {
    2.     
    3. }
    4. class SomeSuperClass {
    5.     
    6. }
    7. class someClass: SomeSuperClass, SomeProtocol {
    8.     required override init() {
    9.         
    10.     }
    11. }
    1. protocol TcpProtocol {
    2.     init(no1: Int)
    3.     func add(count: Int)
    4. }
    5. class MainClass {
    6.     var no1: Int  //局部变量
    7.     init(no1: Int) {
    8.         self.no1 = no1  //初始化
    9.     }
    10. }
    11. class SubClass: MainClass, TcpProtocol {
    12.     func add(count: Int) {
    13.         print("lalallal")
    14.     }
    15.     var no2: Int
    16.     init(no1: Int, no2: Int) {
    17.         self.no2 = no2
    18.         super.init(no1: no1)
    19.     }
    20.     // 因为遵循协议,需要加上"required"; 因为继承自父类,需要加上"override"
    21.     required override convenience init(no1: Int) {
    22.         //便利构造函数 先调用本类的指定初始化器
    23.         self.init(no1: no1, no2: 0)
    24.     }
    25. }
    26. let res = MainClass(no1: 10)
    27. let show = SubClass(no1: 20, no2: 30)
    28. print("res is: \(res.no1)")   // 10
    29. print("res is: \(show.no1)")   // 20
    30. print("res is: \(show.no2)")   // 30


    将协议作为类型

    • 在函数、方法或者初始化器里作为形式参数类型或者返回类型;
    • 作为常量、变量或者属性的类型;
    • 作为数组、字典或者其他存储器的元素的类型。
    1. //协议类型
    2. protocol Generator {
    3.     associatedtype member
    4.     func next() -> member?
    5. }
    6. var items = [10, 20, 30].makeIterator()
    7. while let x = items.next() {
    8.     print(x)
    9. }
    10. /**  输出:
    11.  10
    12.  20
    13.  30
    14.  */
    15. for list in [1, 2, 3].map({ i in i * 5 }) {
    16.     print(list)
    17. }
    18. /**
    19.  输出:
    20.  5
    21.  10
    22.  15
    23.  */


    协议继承

    • 协议可以继承一个或者多个其他协议并且可以在它继承的基础之上添加更多要求。协议继承的语法与类继承的语法相似,只不过可以选择列出多个继承的协议,使用逗号分隔。
    1. protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
    2.     // 协议定义
    3. }
    1. //实例
    2. protocol Classa {
    3.     var no1: Int{ get set }
    4.     func calculate(sum: Int)
    5. }
    6. protocol Result {
    7.     func print(target: Classa)
    8. }
    9. class Student: Result {
    10.     func print(target: Classa) {
    11.         target.calculate(sum: 1)
    12.     }
    13. }
    14. class Classb: Result {
    15.     func print(target: Classa) {
    16.         target.calculate(sum: 5)
    17.     }
    18. }
    19. class Student2: Classa {
    20.     var no1: Int = 10
    21.     func calculate(sum: Int) {
    22.         no1 -= sum
    23.         print("学生尝试 \(sum)次通过")
    24.         
    25.         if no1 <= 0 {
    26.             print("学生缺席考试")
    27.         }
    28.     }
    29. }
    30. class Player {
    31.     var stmark: Result!
    32.     init(stmark: Result!) {
    33.         self.stmark = stmark
    34.     }
    35.     func print(target: Classa) {
    36.         stmark.print(target: target)
    37.     }
    38. }
    39. var marks = Player(stmark: Student())
    40. var marksec = Student2()
    41. marks.print(target: marksec)
    42. marks.print(target: marksec)
    43. marks.print(target: marksec)
    44. marks.stmark = Classb()
    45. marks.print(target: marksec)
    46. marks.print(target: marksec)
    47. marks.print(target: marksec)
    48. /**输出结果:
    49.  学生尝试 1次通过
    50.  学生尝试 1次通过
    51.  学生尝试 1次通过
    52.  学生尝试 5次通过
    53.  学生尝试 5次通过
    54.  学生缺席考试
    55.  学生尝试 5次通过
    56.  学生缺席考试
    57. */


    类专用的协议

    • 通过添加 AnyObject 关键字到协议的继承列表,你就可以限制协议只能被类类型采纳(并且不是结构体或者枚举)。
    • 可以在协议的继承列表中,通过添加class关键字,限制协议只能适配到类(class)类型。
    • 该class关键字必须是第一个出现在协议的继承列表中,其后,才是其他继承协议。格式如下:
    1. protocol SomeProtocol {
    2.     
    3. }
    4. protocol someClasslOnlyProtocol: AnyObject, SomeProtocol {
    5.     // AnyObject 可以限制协议只能被类类型采纳
    6. }
    1. protocol tcpProtocol {
    2.     init(no1: Int)
    3. }
    4. class MainClass {
    5.     var no1: Int
    6.     init(no1: Int) {
    7.         self.no1 = no1
    8.     }
    9. }
    10. class SubClass: MainClass, tcpProtocol {
    11.     var no2: Int
    12.     init(no1: Int, no2: Int) {
    13.         self.no2 = no2
    14.         super.init(no1: no1)  //调用父类的指定初始化器
    15.     }
    16.      // 因为遵循协议,需要加上"required"; 因为继承自父类,需要加上"override"
    17.     required override convenience init(no1: Int) {
    18.         self.init(no1: no1, no2: 0)  //便捷初始化器需要先调用本类的指定初始化器
    19.     }
    20. }
    21. let res = MainClass(no1: 20)
    22. let show = SubClass(no1: 30, no2: 50)
    23. print("res is: \(res.no1)")
    24. print("res is: \(show.no1)")
    25. print("res is: \(show.no2)")
    26. /**
    27.  res is: 20
    28.  res is: 30
    29.  res is: 50
    30.  */


    协议组合

    • 可以使用协议组合来复合多个协议到一个要求里。协议组合行为就和你定义的临时局部协议一样拥有构成中所有协议的需求。协议组合不定义任何新的协议类型。
    • 协议组合使用 SomeProtocol & AnotherProtocol 的形式。你可以列举任意数量的协议,用和符号连接 ( & ),使用逗号分隔。除了协议列表,协议组合也能包含类类型,这允许你标明一个需要的父类。
    1. //协议组合
    2. protocol Named {
    3.     var name: String { get }
    4. }
    5. protocol Aged {
    6.     var age: Int { get }
    7. }
    8. struct Person: Named, Aged {
    9.     var name: String
    10.     var age: Int
    11. }
    12. func WishHappyBirthday(to celebrator: Named & Aged) {  //使用 & 符号连接多个协议
    13.     print("Happy birthday! \(celebrator.name), you are \(celebrator.age)")
    14. }
    15. let person = Person(name: "zhangsan", age: 14)
    16. WishHappyBirthday(to: person)  // 打印: Happy birthday, zhangsan, you are 14


    可选协议要求

    • 你可以给协议定义可选要求,这些要求不需要强制遵循协议的类型实现。可选要求使用 optional 修饰符作为前缀放在协议的定义中。可选要求允许你的代码与 Objective-C 操作。 协议和可选要求必须使用 @objc 标志标记。注意 @objc 协议只能被继承自 Objective-C 类或 其他 @objc 类采纳。它们不能被结构体或者枚举采纳。
  • 相关阅读:
    信号量(信号量操作 & 基于信号量实现的生产者消费者模型)
    .NET Conf China 2023 活动纪实 抢先看
    如何通过工单管理系统提高服务质量和客户满意度?
    Springboot企业人力资源管理系统的开发与数据挖掘t1law计算机毕业设计-课程设计-期末作业-毕设程序代做
    HTTP超文本传输协议详解
    vite+vue3+ts+eslint配置问题
    Redis集群
    javaweb-SpringBoot基础
    C++ Reference: Standard C++ Library reference: C Library: cmath: ceil
    Cilium系列-13-启用XDP加速及Cilium性能调优总结
  • 原文地址:https://blog.csdn.net/xiaobo0134/article/details/126171744