iOS.& Swift Books Swift学徒

16
协议 由ehab yosry amer撰写

在本书中,您已经了解了三种命名类型:structs,classes和enums。有一个名为的类型来了解: 协议 .

与其他命名类型不同,协议不会直接定义您实例化的任何内容。相反,它们定义了实际混凝土类型的界面或蓝图 符合 到。使用协议,您可以定义具体类型Go和实现的常见属性和行为。

您已经在本书开头使用了幕后的协议。在本章中,您将了解协议的详细信息,并了解为什么它们是迅速的核心。

介绍议定书

当您执行任何其他命名类型时,您可以定义一个协议。在游乐场输入以下内容:

 协议  Vehicle {
  func accelerate()
  func stop()
}

The keyword 协议 是 followed by the name of the protocol, followed by the curly braces with the members of the protocol inside. The big difference you’ll notice is that the protocol 不包含任何实施.

That means you can’t instantiate a Vehicle directly:

相反,您使用协议来强制执行方法和属性 其他 类型。你在这里定义的是类似的东西 主意 车辆 - 这是可以加速和停止的东西。

协议语法

可以是一个协议 采用 通过类,结构或枚举 - 当另一个类型采用协议时,需要实现协议中定义的方法和属性所必需的。一旦类型实现协议的所有成员,就会说该类型 符合 to the protocol.

class Unicycle: Vehicle {
  var peddling = false

  func accelerate() {
    peddling = true
  }

  func stop() {
    peddling = false
  }
}

协议中的方法

In the Vehicle 协议 above, you define a pair of methods, accelerate() and stop(), that all types conforming to Vehicle must implement.

enum Direction {
  case left
  case right
}

protocol DirectionalVehicle {
  func accelerate()
  func stop()
  func turn(_ direction: Direction)
  func description() -> String
}
 协议  OptionalDirectionVehicle {
  // Build error!
  func turn(_ direction: Direction = .left)
}
 协议  OptionalDirectionVehicle {
  func turn()
  func turn(_ direction: Direction)
}

协议中的属性

您还可以在协议中定义属性:

 协议  VehicleProperties {
  var weight: Int { get }
  var name: String { get set }
}

协议中的初始化者

虽然协议本身无法初始化,但它们可以声明符合类型应该具有的初始化者:

 协议  Account {
  var value: Double { get set }
  init(initialAmount: Double)
  init?(transferAccount: Account)
}
class BitcoinAccount: Account {
  var value: Double
  required init(initialAmount: Double) {
    value = initialAmount
  }
  required init?(transferAccount: Account) {
    guard transferAccount.value > 0.0 else {
      return nil
    }
    value = transferAccount.value
  }
}

var accountType: Account.Type = BitcoinAccount.self
let account = accountType.init(initialAmount: 30.00)
let transferAccount = accountType.init(transferAccount: account)!

协议继承

The Vehicle 协议 contains a set of methods that could apply to any type of vehicle, such as a bike, a car, a snowmobile or even an airplane!

 协议  WheeledVehicle: Vehicle {
  var numberOfWheels: Int { get }
  var wheelSize: Double { get set }
}

迷你练习

  1. Create a protocol Area that defines a read-only property area of type Double.
  2. Implement Area with structs representing Square, Triangle and Circle.
  3. Add a circle, a square and a triangle to an array. Convert the array of shapes to an array of areas using map.

实施协议

在您已经看到的时候,当您将类型声明为符合协议时,必须实现 全部 协议中宣布的要求:

class Bike: Vehicle {
  var peddling = false
  var brakesApplied = false

  func accelerate() {
    peddling = true
    brakesApplied = false
  }

  func stop() {
    peddling = false
    brakesApplied = true
  }
}

实施属性

Recall that properties in protocols come with a get and possibly a set requirement and that a conforming type must conform to 至少 these requirements.

class Bike: WheeledVehicle {

  let numberOfWheels = 2
  var wheelSize = 16.0

  var peddling = false
  var brakesApplied = false

  func accelerate() {
    peddling = true
    brakesApplied = false
  }

  func stop() {
    peddling = false
    brakesApplied = true
  }
}

协议中的关联类型

您还可以添加一个 相关类型 as a protocol member. When using associatedtype in a protocol, you’re simply stating there 本协议中使用的类型,而无需指定该类型的类型。这取决于协议,决定应该是什么。

 协议  WeightCalculatable {
  associatedtype WeightType
  var weight: WeightType { get }
}
class HeavyThing: WeightCalculatable {
  // This heavy thing only needs integer accuracy
  typealias WeightType = Int

  var weight: Int { 100 }
}

class LightThing: WeightCalculatable {
  // This light thing needs decimal places
  typealias WeightType = Double

  var weight: Double { 0.0025 }
}
// Build error!
// protocol 'WeightCalculatable' can only be used as a generic
// constraint because it has Self or associated type requirements.
let weightedThing: WeightCalculatable = LightThing()

实现多种协议

一个类只能从单个类继承 - 这是“单个继承”的属性。相比之下,可以使类(结构或枚举)符合您喜欢的协议!

 协议  Wheeled {
  var numberOfWheels: Int { get }
  var wheelSize: Double { get set }
}

class Bike: Vehicle, Wheeled {
  // Implement both Vehicle and Wheeled
}

协议组成

在上一节中,您学习了如何实现多个协议。有时您需要一个函数来拍摄必须符合多个协议的数据类型。那就是在哪里 协议组成 comes in. Imagine you need a function that needs access to the Vehicle protocol’s stop() function and the Wheeled protocol’s numberOfWheels property. You can do this using the & composition operator.

func roundAndRound(transportation: Vehicle & Wheeled) {
    transportation.stop()
    print("The brakes are being applied to
          \(transportation.numberOfWheels) wheels.")
}

roundAndRound(transportation: Bike())
// The brakes are being applied to 2 wheels.

延期&协议一致性

You can also adopt protocols using extensions. This language feature lets you add协议一致性 to types you don’t necessarily own. Consider the simple example below, which adds a custom protocol to String:

 协议  Reflective {
  var typeName: String { get }
}

extension String: Reflective {
  var typeName: String {
    "I’m a String"
  }
}

let title= "Swift学徒!"
title.typeName // I’m a String
class AnotherBike: Wheeled {
  var peddling = false
  let numberOfWheels = 2
  var wheelSize = 16.0
}

extension AnotherBike: Vehicle {
  func accelerate() {
    peddling = true
  }
  
  func stop() {
    peddling = false
  }
}

需要引用语义

可以通过值类型(结构和枚举)和引用类型(类)来采用协议,因此您可能想知道协议是否具有引用或值语义。

 协议  Named {
  var name: String { get set }
}

class ClassyName: Named {
  var name: String
  init(name: String) {
    self.name = name
  }
}

struct StructyName: Named {
  var name: String
}
var named: Named = ClassyName(name: "Classy")
var copy = named

named.name = "Still Classy"
named.name // Still Classy
copy.name // Still Classy
named = StructyName(name: "Structy")
copy = named

named.name = "Still Structy?"
named.name // Still Structy?
copy.name // Structy
 协议  Named: AnyObject {
  var name: String { get set }
}

协议:不仅仅是袋子语法

如您所见,协议允许您指定许多符合类型的语法要求。但是,他们不能(永远不会)让您指定编译器检查的每个可想象要求。例如,协议可能需要指定用于操作的复杂性要求(O(1)与O(n))。它可以通过在评论中陈述它来实现这一点。您需要了解协议正确符合正确的所有这些要求。这一现实导致克制的克制该协议不仅仅是编译器可以检查的语法的袋子。

标准库中的协议

SWIFT标准库以可能为您感到惊讶的方式广泛使用协议。了解SWIFT中的角色协议播放可以帮助您编写干净,解耦“SWIFTY”代码。

等等

Some of the simplest code compares two integers with the == operator:

let a = 5
let b = 5

a == b // true
let swiftA = "Swift"
let swiftB = "Swift"

swiftA == swiftB // true
class Record {
  
  var wins: Int
  var losses: Int
    
  init(wins: Int, losses: Int) {
      self.wins = wins
      self.losses = losses
  }
}

let recordA = Record(wins: 10, losses: 5)
let recordB = Record(wins: 10, losses: 5)

recordA == recordB // Build error!
 协议  Equatable {
  static func ==(lhs: Self, rhs: Self) -> Bool
}
extension Record: Equatable {
  static func ==(lhs: Record, rhs: Record) -> Bool {
    lhs.wins == rhs.wins &&
    lhs.losses == rhs.losses
  }
}
recordA == recordB // true

可比

A subprotocol of 等等 可比 :

 协议  Comparable: Equatable {
  static func <(lhs: Self, rhs: Self) -> Bool
  static func <=(lhs: Self, rhs: Self) -> Bool
  static func >=(lhs: Self, rhs: Self) -> Bool
  static func >(lhs: Self, rhs: Self) -> Bool
}
extension Record: Comparable {
  static func <(lhs: Record, rhs: Record) -> Bool {
    if lhs.wins == rhs.wins {
      return lhs.losses > rhs.losses
    }
    return lhs.wins < rhs.wins
  }
}

“免费”功能

While == and < are useful in their own right, the Swift library provides you with many “free” functions and methods for types that conform to 等等 and 可比 .

let teamA = Record(wins: 14, losses: 11)
let teamB = Record(wins: 23, losses: 8)
let teamC = Record(wins: 23, losses: 9)
var leagueRecords = [teamA, teamB, teamC]

leagueRecords.sort()
// {wins 14, losses 11}
// {wins 23, losses 9}
// {wins 23, losses 8}
leagueRecords.max() // {wins 23, losses 8}
leagueRecords.min() // {wins 14, losses 11}
leagueRecords.starts(with: [teamA, teamC]) // true
leagueRecords.contains(teamA) // true

其他有用的协议

在学习时 全部的 Swift标准库对您的成功至关重要,作为SWIFT开发人员,您将在几乎任何项目中发现有一些其他重要的协议。

哈希布扎尔
class Student {
  let email: String
  let firstName: String
  let lastName: String

  init(email: String, firstName: String, lastName: String) {
    self.email = email
    self.firstName = firstName
    self.lastName = lastName
  }
}

extension Student: Hashable {
  static func ==(lhs: Student, rhs: Student) -> Bool {
    lhs.email == rhs.email &&
    lhs.firstName == rhs.firstName &&
    lhs.lastName == rhs.lastName
  }

  func hash(into hasher: inout Hasher) {
    hasher.combine(email)
    hasher.combine(firstName)
    hasher.combine(lastName)
  }
}
let john = Student(email: "[email protected]",
                   firstName: "Johnny",
                   lastName: "Appleseed")
let lockerMap = [john: "14B"]
可识别
extension Student: Identifiable {
  var id: String {
    email
  }
}

CustomStringConvertibly.

The very handy CustomStringConvertibly. 协议 helps you log and debug instances.

print(john)
// Student
 协议  CustomStringConvertibly. {
  var description: String { get }
}
extension Student: CustomStringConvertibly. {
  var description: String {
    "\(firstName) \(lastName)"
  }
}
print(john)
// Johnny Appleseed

挑战

在继续前进之前,以下是测试您对协议知识的挑战。最好尝试自己解决它,但是,一如既往,如果你被困,可以使用解决方案。

宠物店任务

在宠物商店创建一系列协议,狗,猫,鱼和鸟类。

关键点

  • 协议定义了课程,结构和枚举的合同 采纳 .
  • 通过采用协议,需要一种类型 符合 通过实现协议的所有方法和属性来对协议。
  • 类型可以采用任何数量的协议,该协议允许通过子类化不允许的准多重继承。
  • 您可以使用协议采用和一致性的扩展。
  • The Swift standard library uses protocols extensively. You can use many of them, such as 等等 and 哈希布扎尔 , on your own named types.

有一个技术问题?想报告一个错误吗? 您可以向官方书籍论坛中的书籍作者提出问题和报告错误 这里 .

有反馈分享在线阅读体验吗? 如果您有关于UI,UX,突出显示或我们在线阅读器的其他功能的反馈,您可以将其发送到设计团队,其中表格如下所示:

© 2021 Razeware LLC

您可以免费读取,本章的部分显示为 混淆了 文本。解锁这本书,以及我们整个书籍和视频目录,带有Raywenderlich.com的专业订阅。

现在解锁

要突出或记笔记,您需要在订阅中拥有这本书或自行购买。