协议(Protocols)

协议:规定了用来实现某一特定 工作 或者 功能 所必须的 方法 和 属性。

类、结构体,枚举 都可以遵循协议。(协议要求类,结构体,枚举必须实现协议要求的 属性 和 方法。)

任意能够满足协议要求的类型被称为 遵循 这个协议。

协议除了遵守协议还可以进行协议扩展,来实现一些附加的特殊功能。

协议语法:

protocol SomeProtocol {
      // 协议内容
}

遵循某个协议
遵循某个协议,需要在类型名称后面加上协议名称,中间以冒号, 作为类型定义的一部分。

struct SomeStructure : FistProtocol , AnotherProtocol {
        // 结构体的内容( 属性 和 方法)
}

类在遵守协议的同时拥有父类,父类名放在协议的前面,以逗号进行分隔。

class SomeClass: SomeSuperClasses, FistProtocol , AnotherProtocol {
      // 类的内容
}

协议对属性的规定

协议可以规定其 遵守者 提供特定名称和类型的 实例属性(instance property) 或者 类属性(type property)

协议是不能指定 属性 是 **存储属性 ** 还是 ** 计算属性 **。
协议可以指定 属性可读的 还是 ** 可读可写的 **。

  1. 协议要求属性是可读可写的,那么属性不能是 常量属性只读的计算型属性
  1. 如果协议只 要求属性是可读的,那么这个属性 不仅 是可读的,可以通过代码修改为可读可写的。

  2. 协议总是用 var 关键字来声明 变量属性,在类型名称后加上 { set get } 来表示属性是可读可写的,用 { get } 来表示可读:

protocol SomeProtocol {
      var mustBeSettable: Int { get set }
      var doseNotNeedToBeSettable: Int  { get }
}
  1. 类型属性的定义: 协议中总是使用 ** static ** 关键字作为前缀。当类类型采纳协议时,除了 使用 static 关键字,还可以使用 class 关键字来声明类属性:
protocol AnotherProtocol {
        // 类型属性
        static var someTypeProperty: Int {  get set }
}
protocol FullNamed {
        // 实例属性
        var fullName: String { get }
}

协议对于方法的规定

协议要求采纳协议的类型实现某些指定的 实例方法类方法

  1. 可以在协议中定义具有可变参数的方法(和普通方法的定义一样)
  1. 不支持为协议中的方法的参数提供默认值

  2. 协议中定义方法,使用 static关键字作为前缀。 当类类型采纳协议时,除了 使用 static 关键字,还可以使用 class 关键字最为前缀

protocol SomeProtocol {
        // 类型方法
        static fun someTypeMethod()
      
        // 实例方法
        fun random() -> Double
}

Mutating 关键字的使用

Mutating 主要是用在值类型(结构体 和 枚举)实例方法中。
将 mutating 关键字作为方法的前缀,写在 func 关键字之前。表示 可以在该方法中修改他所属的实例以及实例的任意属性值

结构体 和 枚举 是值类型,默认情况下值类型的属性是不能再他的实例方法中进行修改。

在协议中定义实例方法的时候,当方法会改变采纳该协议的类型的实例的时候,在定义协议方法的时候需要在方法前加 mutating 关键字。(主要是给枚举和结构体准备的)

在协议中定义了实例方法,如果遵协议的类型是 枚举结构体,那么就需要在协议方法的前面添加 mutating 关键字。

构造器要求

协议可以要求采纳协议的类型实现指定的 构造器

protocol SomeProtocol {
        init(someParamer: Int)
}

构造器要求在类中的实现
采纳协议的类实现构造器,无论是指定构造器,还是便利构造器。都需要在构造器实现上 ** required ** 修饰符:

class SomeClass: SomeProtocol {
        required init(someParameter:Int){
                // 构造器实现  
      }
}

可选协议

在协议中使用 optional 关键字作为前缀来定义可选要求。

使用可选要求时,可选的方法或函数的类型会自动变为可选。
** 是整个函数的类型是可选的 **

协议合成
当需要同时采纳多个协议,我们可以使用协议合成。protocol <SomeProtocol, AnotherProtocol> 的格式进行组合。协议组合主要用在函数方法传参的地方。

protocol Named {
    var name: String { get }
}
protocol Aged {
    var age: Int { get }
}
struct Person: Named, Aged {
    var name: String
    var age: Int
}
func wishHappyBirthday(celebrator: protocol<Named, Aged>) {
    print("Happy birthday \(celebrator.name) - you're \(celebrator.age)!")
}
let birthdayPerson = Person(name: "Malcolm", age: 21)
wishHappyBirthday(birthdayPerson)
// 打印 “Happy birthday Malcolm - you're 21!”

协议扩展

协议可以通过 扩展 来为采纳协议的类型提供属性、方法以及下标的实现
你可以基于协议本身来实现这些功能, 而无需再每个采纳协议的类型中都重复同样实现。也无需使用全局函数。

extension RandomNumberGeneraator {
        func randomBool() -> Bool {
                return random() > 0.5 
        }
}

** 通过协议扩展,所有采纳协议的类型,都能自动获得这个扩展所增加的方法实现,无需任何修改。 **

let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
// 打印 “Here's a random number: 0.37464991998171”
print("And here's a random Boolean: \(generator.randomBool())")
// 打印 “And here's a random Boolean: true”

** 提供默认实现 **
协议可以通过 扩展 来为采纳协议的类型提供属性、方法以及下标提供 默认 的实现

采纳协议的类型提供了自己的实现,那么自定义的实现将会替代扩展中的默认实现。

通过协议扩展为协议要求提供的默认实现和可选的协议要求不同:

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

推荐阅读更多精彩内容