Swift协议

时间:2020-02-23 14:44:03  来源:igfitidea点击:

我们将在本教程中讨论并研究另一个重要元素,即Swift协议。
此外,我们将研究Swift 4带来的变化。

Swift协议

苹果文档定义协议的方法如下。

如果上述定义对您没有完全意义,请使用一个简单的定义。

Swift协议基本上是类/结构/枚举可以用来实现一组特定方法和属性的契约。

换句话说,要成为THAT,您需要使用THIS。

Swift协议语法

Swift协议的语法类似于类,结构和枚举。

protocol MyFirstProtocol{
  
  //Properties and methods declarations go in here.
  
}

如下所示,类,结构和枚举可以通过将协议放在冒号后面来采用该协议。

class A : MyFirstProtocol{
  //Enter your code
}

struct B : MyFirstProtocol{
  //Enter your code
}

enum E : MyFirstProtocol {
  //Enter your code
}
  • 如果协议不止一种,则以逗号分隔。

  • 如果类具有超类,则超类名称将首先出现,然后是协议。

protocol FirstProtocol{
  //Properties and methods declarations goes in here.
}

protocol SecondProtocol{
  //Properties and methods declarations goes in here.
}

class S {
  //Let's say this is the super class.
}

class A : S,FirstProtocol, SecondProtocol{
  //Enter your code
}

struct B : FirstProtocol, SecondProtocol{
  //Enter your code
}

enum E : FirstProtocol, SecondProtocol {
  //Enter your code
}

当类/结构或者枚举符合协议时,它必须实现其所有方法进行编译。
让我们通过XCode启动Playground,深入研究协议的深层次。

Swift协议要求

  • 协议可以包含属性以及符合该协议的类/结构/枚举将实现的方法。

  • 首先,必须将协议内的变量声明为变量,而不是常量(不允许使用" let")。

  • 协议内的属性必须包含关键字" var"。

  • 我们需要明确指定属性的类型(" {get}"或者" {get set}")。

以下是这样做的两种方法。

protocol FirstProtocol{
  var age : Int {get set} //read-write property
  var year : Int {get}  //optional write property
}
  • {get set}计算的属性既可读又可写。
    对于{get},它不需要设置。

如果用户只想确保其可读性,可以将属性更改为类中的常量。

class A : FirstProtocol{

  var age = 23
  var year = 2016   
}

var a = A()
a.year = 2016
print(a.year) //prints 2016

//Alternative case
class A : FirstProtocol{

  var age = 23
  let year = 2016 //now this is a constant.
}
  • 协议内部的功能无需主体即可定义。

  • 函数中的默认值是不允许的。

  • 也可以使用带有可变参数的函数。

  • 可以通过在关键字中添加关键字mutating来定义变量功能。

  • 在为类编写该方法的实现时,我们不需要编写mutating关键字。

  • mutating关键字仅由结构和枚举使用。

Swift通讯协定范例

让我们来看一个使用上述快速协议概念的示例。

protocol CSDegree{
  var cgpa : Double {get set}
  var name : String? {get set}
  var coursesCompleted : Bool? {get set}
  func haveYouCompletedOperatingSystems() -> Bool
  func haveYouCompletedJavaCourse() -> Bool
  func printAllGrades(grade: String...)
  func degreeGranted()
  mutating func hackToGetMyDegree()
}

class Student : CSDegree {
  
  var cgpa: Double = 2
  var name: String?
  var coursesCompleted: Bool?
  func haveYouCompletedJavaCourse() -> Bool {
      return coursesCompleted ?? false
  }
  func haveYouCompletedOperatingSystems() -> Bool {
      return coursesCompleted ?? false
  }
  
  func printAllGrades(grade: String...) {
      
      if let n = name{
      print("Grades of \(n) in all the courses are \(grade)")
      }
      else{
          print("Student not found")
      }
  }
  
  func degreeGranted() {
      if haveYouCompletedJavaCourse() && haveYouCompletedOperatingSystems() && cgpa > 4
      {
          print("Congrats, you've completed Bachelors in Computer Science")
      }
      else{
          print("Sorry, you won't be granted your degree this year")
      }
  }
  
  func hackToGetMyDegree() {
      cgpa = 8.5
      coursesCompleted = true
  }
  
}

var s = Student()
s.cgpa = 6.93
s.name = "Anupam"
s.coursesCompleted = false
print(s.name ?? "Not found") //prints "Anupam\n"
s.haveYouCompletedOperatingSystems()
s.haveYouCompletedJavaCourse()
s.degreeGranted() //Sorry, you won't be granted your degree this year
s.printAllGrades(grade: "A","D") //prints Grades of Anupam in all the courses are ["A", "D"]

糟糕!由于我的" coursesCompleted"布尔值属性为false,因此" degreeGranted()"方法并没有给我我的学位。
让我们使用mutating函数来更改属性,如下所示。

s.hackToGetMyDegree()
s.degreeGranted() //prints Congrats, you've completed Bachelors in Computer Science

同样,我们可以在下面的结构中使用该协议。

struct StudentStruct : CSDegree {
  var cgpa: Double = 8.5
  var name: String?
  var coursesCompleted: Bool?
  func haveYouCompletedJavaCourse() -> Bool {
      return coursesCompleted ?? false
  }
  func haveYouCompletedOperatingSystems() -> Bool {
      return coursesCompleted ?? false
  }
  
  func printAllGrades(grade: String...) {
      
      if let n = name{
          print("Grades of \(n) in all the courses are \(grade)")
      }
      else{
          print("Student not found")
      }
  }
  
  func degreeGranted() {
      if haveYouCompletedJavaCourse() && haveYouCompletedOperatingSystems() && cgpa > 4
      {
          print("Congrats, you've completed Bachelors in Computer Science")
      }
      else{
          print("Sorry, you won't be granted your degree this year")
      }
  }
  
  mutating func hackToGetMyDegree() {
      cgpa = 8.5
      coursesCompleted = true
  }
}

var s1 = StudentStruct()
s1.cgpa = 3.9
s1.name = "Anupam"
s1.coursesCompleted = false
print(s1.name ?? "Not found")
s1.haveYouCompletedOperatingSystems()
s1.haveYouCompletedJavaCourse()
s1.degreeGranted()
s1.printAllGrades(grade: "A","D")
s1.hackToGetMyDegree()
s1.degreeGranted()

带初始化器的Swift协议

我们可以在符合条件的类上实现快速协议初始化程序要求,作为指定的初始化程序或者便捷的初始化程序。
为此,我们必须在符合条件的类的" init"之前添加关键字" required",如下所示:

protocol MyInitialiser {
  init(name: String)
}

class TestInitProtocol : MyInitialiser{
  
  required init(name: String) {
      print("Welcome to \(name)")
  }
}
var t = TestInitProtocol(name: "theitroad") //prints Welcome to theitroad\n"

//Convenience initializer example
protocol MyInitialiser {
  init(name: String)
}

class TestInitProtocol : MyInitialiser{
  
  convenience required init(name: String) {
      print("Welcome to \(name)")
      self.init()
  }
}
var t = TestInitProtocol(name: "theitroad")

将协议初始化程序与SuperClass的初始化程序一起使用

protocol MyInitialiser {
  init()
}

class SuperClass{
  
  init() {
  }
}

class TestInitProtocol : SuperClass, MyInitialiser{
  

      required override init() {
          print("required from protocol, override from superclass")
  
  }
  
}
var t = TestInitProtocol()

将Swift协议与扩展一起使用

我们可以使用扩展协议来在现有类型上添加新属性,方法。

protocol Greet
{
  var str : String {get}
}
class Name{
  var name: String?
}
extension Name : Greet{
  
  var str : String {
      return "Hello, \(name ?? "Mr. X")"
  }
}

var n = Name()
n.name = "Anupam"
print(n.str) //prints "Hello, Anupam\n"

协议扩展协议可以扩展以提供方法和属性实现以符合类型。
这样,我们也可以为协议中的属性指定默认值,如下所示。

protocol WebsiteName{

  var name : String {get}
}
extension WebsiteName{
  
  var name : String {
      return "theitroad"
  }
}
class W : WebsiteName {
}
var w = W()
print(w.name)

带枚举的Swift协议

考虑以下情况,其中在枚举中实现协议。

protocol ModifyCurrentDay
{
  func currentDay() -> String
  mutating func firstDayOfTheWeek()
  
}

enum DaysOfAWeek: String,ModifyCurrentDay{
  
  case Sunday
  case Monday
  case Tuesday
  case Wednesday
  
  func currentDay()->String{
      return self.rawValue
  }
  
  mutating func firstDayOfTheWeek() {
      self = .Sunday
  }
}

var day = DaysOfAWeek.Wednesday
print(day.currentDay()) //prints "Wednesday\n"
day.firstDayOfTheWeek()
print(day.currentDay()) //prints "Sunday\n"

Swift协议中的继承

一个协议可以继承一个或者多个其他协议。
这对于在协议顶部添加更多要求很有用。
这样做,继承其他协议的协议就必须在类/结构/枚举中实现它们的方法和属性。
协议继承的语法类似于类继承,如下所示

protocol A {
  var length : Int {get set}
}

protocol B {
  var breadth : Int {get set}
}

protocol C : A, B {
  var height : Int {get set}
}

class Volume : C {
  
  var height : Int = 5
  var breadth: Int = 10
  var length: Int = 2
  
  func printVolume() {
      print("Volume is \(height*breadth*length)")
  }
}

var v = Volume()
v.printVolume() //prints 100

为了将协议限制为仅用作类的协议,并且不与结构/枚举一起使用,请使用以下语法。

protocol C : AnyObject, A, B {
  var height : Int {get set}
}

上面的协议不能用于结构体和枚举,因为它扩展了AnyObject。

Swift协议组成

Swift协议也可以用作函数内的类型。
我们可以将多个协议组合为一个组合,并将它们用作函数内的参数,如下所示。
我们可以根据需要列出任意数量的协议,并用&分隔。
单个类类型也可以包含在通常用于指定超类的组合中。

protocol A {
  var length : Int {get set}
}
protocol B {
  var breadth : Int {get set}
}
protocol C {
  var height : Int {get set}
}
struct Volume : A, B, C {
  
  var length: Int
  var breadth: Int
  var height: Int
}

func printVolume(to volume: A & B & C) {
  print("Volume is \(volume.length*volume.breadth*volume.height)")
}
var v = Volume(length : 5, breadth: 5, height: 5)
printVolume(to: v) //prints 125

Swift 4混合协议和类

使用Swift 4,我们现在也可以将协议作为类型与类结合起来,如下所示。

protocol Name{}
protocol Age{}
class Zodiac{}
class Description{}

typealias Z = Name&Age&Zodiac
typealias D = Name&Age&Description

var nameAgeDescription : D
var nameAgeZodiac : Z

在协议组成中添加一个类可以使我们免于为上述协议和基类的不同类型的类创建不同的子类。

可选协议要求

我们可以将协议内的属性或者方法指定为可选。
这些要求不必通过符合协议的类型来实现。
为此,我们需要在关键字协议之前指定关键字" @objc",并为每个可选属性/方法添加关键字" @objc optional",如下所示。

@objc protocol OptionalPro{
  
  @objc optional var name : String{get set}
  @objc optional func printName()
  func helloWord()
  
}

class O : OptionalPro{
  
  func helloWord() {
      print("hello world")
  }
  
  //Compiles fine without the need of implementing the optional requirements.
}