首页 iOS.& Swift Books Swift学徒

14
高级课程 由cosminpupăză撰写

前一章向您介绍了在Swift中定义和使用类的基础知识。类是参考类型,可用于支持传统面向对象的编程。

课程介绍继承,覆盖,多态性,使其适合此目的。这些额外的功能需要特殊考虑初始化,类层次结构,并在内存中了解类生命周期。

本章将向您介绍SWIFT中的课程更精细的课程,并帮助您了解如何创建更复杂的类。

介绍继承

In the previous chapter, you saw a Grade struct and a pair of class examples: Person and Student.

struct Grade {
  var letter: Character
  var points: Double
  var credits: Double
}

class Person {
  var firstName: String
  var lastName: String

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

class Student {
  var firstName: String
  var lastName: String
  var grades: [Grade] = []

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

  func recordGrade(_ grade: Grade) {
    grades.append(grade)
  }
}

It’s not difficult to see that there’s redundancy between Person and Student. Maybe you’ve also noticed that a Student a Person!

This simple case demonstrates the idea behind class inheritance. Much like in the real world, where you can think of a student as a person, you can represent the same relationship in code by replacing the original Student class implementation with the following:

class Student: Person {
  var grades: [Grade] = []

  func recordGrade(_ grade: Grade) {
    grades.append(grade)
  }
}

In this modified example, the Student class now 继承 from Person, indicated by a colon after the naming of Student, followed by the class from which Student inherits, which in this case is Person.

Through inheritance, Student automatically gets the properties and methods declared in the Person class. In code, it would be accurate to say that a Student 是-A. Person.

With much less duplication of code, you can now create Student objects that have all the properties and methods of a Person:

let john = Person(firstName: "Johnny", lastName: "Appleseed")
let jane = Student(firstName: "Jane", lastName: "Appleseed")

john.firstName // "John"
jane.firstName // "Jane"

Additionally, only the Student object will have all of the properties and methods defined in Student:

let history = Grade(letter: "B", points: 9.0, credits: 3.0)
jane.recordGrade(history)
// john.recordGrade(history) // john is not a student!

从另一个类继承的类被称为a 子类 或者 派生类,它继承的类被称为a 超类 或者 基类.

子类化规则很简单:

  • Swift类可以从另一个类继承,一个被称为的概念 单遗传.
  • 对子类化的深度没有限制,这意味着您可以从一个类中子类 一个子类,如下:
class BandMember: Student {
  var minimumPracticeTime = 2
}

class OboePlayer: BandMember {
  // This is an example of an override, which we’ll cover soon.
  override var minimumPracticeTime: Int {
    get {
      super.minimumPracticeTime * 2
    }
    set {
      super.minimumPracticeTime = newValue / 2
    }
  }
}

一组亚类被称为一个 类层次结构. In this example, the hierarchy would be OboePlayer -> BandMember -> Student -> Person. A class hierarchy is analogous to a family tree. Because of this analogy, a superclass is also called the 父母类 它的 儿童班.

多态性

The Student/Person relationship demonstrates a computer science concept known as 多态性。简而言之,多态性是基于上下文不同地处理物体的编程语言。

func phonebookName(_ person: Person) -> String {
  "\(person.lastName), \(person.firstName)"
}

let person = Person(firstName: "Johnny", lastName: "Appleseed")
let oboePlayer = OboePlayer(firstName: "Jane",
                            lastName: "Appleseed")

phonebookName(person) // Appleseed, Johnny
phonebookName(oboePlayer) // Appleseed, Jane

运行时层次结构检查

Now that you are coding with polymorphism, you’ll likely find situations where the specific type behind a variable can be different. For instance, you could define a variable hallMonitor as a Student:

var hallMonitor = Student(firstName: "Jill", 
                          lastName: "Bananapeel")
hallMonitor = oboePlayer
oboePlayer as Student
(oboePlayer as Student).minimumPracticeTime // ERROR: No longer a band member!

hallMonitor as? BandMember
(hallMonitor as? BandMember)?.minimumPracticeTime // 4 (optional)

hallMonitor as! BandMember // Careful! Failure would lead to a runtime crash.
(hallMonitor as! BandMember).minimumPracticeTime // 4 (force unwrapped)
if let hallMonitor = hallMonitor as? BandMember {
  print("This hall monitor is a band member and practices 
         at least \(hallMonitor.minimumPracticeTime) 
         hours per week.")
}
func afterClassActivity(for student: Student) -> String {
  "Goes home!"
}

func afterClassActivity(for student: BandMember) -> String {
  "Goes to practice!"
}
afterClassActivity(for: oboePlayer) // Goes to practice!
afterClassActivity(for: oboePlayer as Student) // Goes home!

继承,方法和覆盖

子类接收在其超类中定义的所有属性和方法以及子类为其定义的任何其他属性和方法。在这种意义上,子类是添加剂。

class StudentAthlete: Student {
  var failedClasses: [Grade] = []

  override func recordGrade(_ grade: Grade) {
    super.recordGrade(grade)

    if grade.letter == "F" {
      failedClasses.append(grade)
    }
  }

  var isEligible: Bool {
    failedClasses.count < 3
  }
}

介绍超级

You may have also noticed the line super.recordGrade(grade) in the overridden method. The super keyword is similar to self, except it will invoke the method in the nearest implementing superclass. In the example of recordGrade(_:) in StudentAthlete, calling super.recordGrade(grade) will execute the method as defined in the Student class.

什么时候打电话给超级

你可能会注意到,正好 什么时候 您称之为Super会对您的重写方法产生重要影响。

覆盖 func recordGrade(_ grade: Grade) {
  var newFailedClasses: [Grade] = []
  for grade in grades {
    if grade.letter == "F" {
      newFailedClasses.append(grade)
    }
  }
  failedClasses = newFailedClasses

  super.recordGrade(grade)
}

防止继承

Sometimes you’ll want to disallow subclasses of a particular class. Swift provides the 最后 keyword for you to guarantee a class will never get a subclass:

最后 class FinalStudent: Person {}
class FinalStudentAthlete: FinalStudent {} // Build error!
class AnotherStudent: Person {
  final func recordGrade(_ grade: Grade) {}
}

class AnotherStudentAthlete: AnotherStudent {
  override func recordGrade(_ grade: Grade) {} // Build error!
}

继承和类初始化

前一章简要介绍了你上课初始化者,它类似于他们的结构对应物。使用子类,关于您如何设置实例有一些更多的考虑因素。

class StudentAthlete: Student {
  var sports: [String]
  // original code
}
class StudentAthlete: Student {
  var sports: [String]

  init(sports: [String]) {
    self.sports = sports
    // Build error - super.init isn’t called before
    // returning from initializer
  }
  // original code
}

class StudentAthlete: Student {
  var sports: [String]

  init(firstName: String, lastName: String, sports: [String]) {
    self.sports = sports
    super.init(firstName: firstName, lastName: lastName)
  }
  // original code
}

两阶段初始化

由于SWIFT的要求,所有存储的属性都有初始值,子类中的初始化者必须遵守SWIFT的惯例 两阶段初始化.

class StudentAthlete: Student {
  var sports: [String]

  init(firstName: String, lastName: String, sports: [String]) {
    // 1
    self.sports = sports
    // 2
    let passGrade = Grade(letter: "P", points: 0.0, 
                          credits: 0.0)
    // 3
    super.init(firstName: firstName, lastName: lastName)
    // 4
    recordGrade(passGrade)
  }
  // original code
}

迷你练习

What’s different in the 两阶段初始化 in the base class Person, as compared to the others?

必需和便利初始化者

您已经知道在类中有多个初始化器,这意味着您可能会呼叫 任何 来自子类的那些初始化者。

class Student {
  let firstName: String
  let lastName: String
  var grades: [Grade] = []

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

  init(transfer: Student) {
    self.firstName = transfer.firstName
    self.lastName = transfer.lastName
  }

  func recordGrade(_ grade: Grade) {
    grades.append(grade)
  }
}
class Student {
  let firstName: String
  let lastName: String
  var grades: [Grade] = []

  required init(firstName: String, lastName: String) {
    self.firstName = firstName
    self.lastName = lastName
  }
  // original code
}
class StudentAthlete: Student {
  // Now required by the compiler!
  required init(firstName: String, lastName: String) {
    self.sports = []
    super.init(firstName: firstName, lastName: lastName)
  }
  // original code
}
class Student {
  convenience init(transfer: Student) {
    self.init(firstName: transfer.firstName, 
              lastName: transfer.lastName)
  }
  // original code
}

迷你练习

Create two more convenience initializers on Student. Which other initializers are you able to call?

何时何时和为什么要分组

本章向您介绍了类继承,以及子类启用的众多编程技术。

class Student: Person {
  var grades: [Grade]
  var sports: [Sport]
  // original code
}

单身责任

在软件开发中,指南称为 单一责任原则 states that any class should have a single concern. In Student/StudentAthlete, you might argue that it shouldn’t be the Student class’s job to encapsulate responsibilities that only make sense to student athletes.

强大的类型

子类创建额外类型。使用Swift的类型系统,您可以根据学生运动员的对象声明属性或行为,而不是普通学生:

class Team {
  var players: [StudentAthlete] = []

  var isEligible: Bool {
    for player in players {
      if !player.isEligible {
        return false
      }
    }
    return true
  }
}

共享基本课程

您可以通过具有互斥行为的类多次将共享基类分组:

// A button that can be pressed.
class Button {
  func press() {}
}

// An image that can be rendered on a button
class Image {}

// A button that is composed entirely of an image.
class ImageButton: Button {
  var image: Image

  init(image: Image) {
    self.image = image
  }
}

// A button that renders as text.
class TextButton: Button {
  var text: String

  init(text: String) {
    self.text = text
  }
}

可扩展性

Sometimes you need to extend the behavior of code you don’t own. In the example above, it’s possible Button 是 part of a framework you’re using, so there’s no way you can modify or extend the source code to fit your specific case.

身份

最后,了解该类和类层次结构模型是什么对象 。如果您的目标是分享行为(什么对象 可以做)在类型之间,更频繁的是,您应该更喜欢在子类上的协议。您将在第16章“协议”中了解协议。

了解班级生命周期

在上一章中,您了解到对象是在内存中创建的,它们存储在堆上。堆上的对象是 不是 自动销毁,因为堆只是一个巨大的内存池。如果没有呼叫堆栈的效用,则没有自动方法来说,可以知道一段内存将不再使用。

var someone = Person(firstName: "Johnny", lastName: "Appleseed")
// Person object has a reference count of 1 (someone variable)

var anotherSomeone: Person? = someone
// Reference count 2 (someone, anotherSomeone)

var lotsOfPeople = [someone, someone, anotherSomeone, someone]
// Reference count 6 (someone, anotherSomeone, 4 references in lotsOfPeople)

anotherSomeone = nil
// Reference count 5 (someone, 4 references in lotsOfPeople)

lotsOfPeople = []
// Reference count 1 (someone)
someone = Person(firstName: "Johnny", lastName: "Appleseed")
// Reference count 0 for the original Person object!
// Variable someone now references a new object

除初系

当对象的参考计数达到零时,Swift从内存中删除对象,并将内存标记为自由。

class Person {
  // original code
  deinit {
    print("\(firstName) \(lastName) is being removed
          from memory!")
  }
}

迷你练习

Modify the Student class to have the ability to record the student’s name to a list of graduates. Add the name of the student to the list when the object is deallocated.

保留周期和弱引用

因为Swift的课程依赖于引用计数将它们从内存中删除,但了解A的概念非常重要 保留周期.

class Student: Person {
  var partner: Student?
  // original code
  deinit {
    print("\(firstName) is being deallocated!")
  }
}

var alice: Student? = Student(firstName: "Alice",
                              lastName: "Appleseed")
var bob: Student? = Student(firstName: "Bob",
                            lastName: "Appleseed")

alice?.partner = bob
bob?.partner = alice
alice = nil
bob = nil
class Student: Person {
  weak var partner: Student?
  // original code
}

开奖结果3d

在继续前进之前,这是您对高级课程的知识测试的核心开奖结果3d。如果您尝试解决它们是您自己,这是最好的,但如果您陷入困境,则可以使用解决方案。在介绍中列出的已预示的源链接下载或亚运床附带。

开奖结果3d1:初始化顺序

Create three simple classes called A, B, and C where C 继承 from B and B 继承 from A. In each class initializer, call print("I’m <X>!") both before and after super.init(). Create an instance of C called c. What order do you see each print() called in?

开奖结果3d2:Deinitialization令

Implement deinit for each class. Create your instance c inside of a do { } scope which will cause the reference count to go to zero when it exits the scope. Which order are the classes deinitialized in?

开奖结果3d3:铸造

Cast the instance of type C to an instance of type A. Which casting operation do you use and why?

开奖结果3d4:亚类与否

Create a subclass of StudentAthlete called StudentBaseballPlayer and include properties for position, number, and battingAverage. What are the benefits and drawbacks of subclassing StudentAthlete in this scenario?

关键点

  • 类继承 是课程最重要的特征之一,并启用 多态性.
  • 子类化 是一个强大的工具,很好地了解何时课程。子类何时从对象框架扩展对象和冷受益的对象子类和超类,但介绍的层次结构。
  • 关键词 覆盖 当您在子类中覆盖一个方法时,清除它。
  • 关键词 最后 可用于防止类被子类化。
  • Swift课程使用 两阶段初始化 作为安全措施,以确保在使用它们之前初始化所有存储的属性。
  • 类实例有自己的生命周期,由他们控制 参考数点.
  • 自动参考计数, 或者 ,自动处理对您的参考计数,但要注意 保留周期.

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

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

© 2021 Razeware LLC

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

现在解锁

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