Chapter 14: Advanced Classes

大綱

Introducing inheritance

  • A Swift class can inherit from only one other class, a concept known as single inheritance.

  • There’s no limit to the depth of subclassing, meaning you can subclass from a class that is also a subclass

    • A chain of subclasses is called a class hierarchy

    • A superclass is also called the parent class of its child class.

Polymorphism

  • polymorphism is a programming language’s ability to treat an object differently based on context.

Runtime hierarchy checks

  • Swift provides the as operator to treat a property or a variable as another type:

    • as: Cast to a specific type that is known at compile time to succeed, such as casting to a supertype.

    • as?: An optional downcast (to a subtype). If the downcast fails, the result of the expression will be nil.

    • as!: A forced downcast. If the downcast fails, the program will crash. Use this rarely, and only when you are certain the cast will never fail.”

  • The optional downcast as? is particularly useful in if let or guard statements

Inheritance, methods and overrides

  • Besides creating their own methods, subclasses can override methods defined in their superclass

  • If your subclass were to have an identical method declaration as its superclass, but you omitted the override keyword, Swift would emit a compiler error

Introducing super

  • The super keyword is similar to self, except it will invoke the method in the nearest implementing superclass

When to call super

  • It’s best practice to call the super version of a method first when overriding. That way, the superclass won’t experience any side effects introduced by its subclas

Preventing inheritance

  • By marking the FinalStudent class final, you tell the compiler to prevent any classes from inheriting.

  • can mark individual methods as final, if you want to allow a class to have subclasses, but protect individual methods from being overridden:

Inheritance and class initialization

  • Initializers in subclasses are required to call super.init because without it, the superclass won’t be able to provide initial states for all its stored properties

Two-phase initialization

  • two-phase initialization.

    • Phase one: Initialize all of the stored properties in the class instance, from the bottom to the top of the class hierarchy. You can’t use properties and methods until phase one is complete.

    • Phase two: You can now use properties and methods, as well as initializations that require the use of self.

Required and convenience initializers

  • it’s possible to have multiple initializers in a class

  • Swift supports this through the language feature known as required initializers

    • This keyword will force all subclasses of Student to implement this initializer.

  • You can also mark an initializer as a convenience initializer

  • A non-convenience initializer is called a designated initializer and is subject to the rules of two-phase initialization.

  • Here’s a summary of the compiler rules for using designated and convenience initializers:

    • A designated initializer must call a designated initializer from its immediate superclass.

    • A convenience initializer must call another initializer from the same class.

    • A convenience initializer must ultimately call a designated initializer.

When and why to subclass

Single responsibility

  • single responsibility principle states that any class should have a single concern.

Strong types

  • Subclassing creates an additional type

Shared base classes

  • It makes sense for Button to be concerned with the press behavior, and the subclasses to handle the actual look and feel of the button.

Extensibility

  • you can subclass Button and add your custom subclass to use with code that’s expecting an object of type Button.

Identity

  • it’s important to understand that classes and class hierarchies model what objects are.

  • If your goal is to share behavior (what objects can do) between types, more often than not you should prefer protocols over subclassing

Understanding the class lifecycle

  • In Swift, the mechanism for deciding when to clean up unused objects on the heap is known as reference counting.

  • When a reference count reaches zero, that means the object is now abandoned since nothing in the system holds a reference to it. When that happens, Swift will clean up the object.

Deinitialization

  • A deinitializer is a special method on classes that runs when an object’s reference count reaches zero, but before Swift removes the object from memory.

    • What you do in an deinitializer is up to you. Often you’ll use it to clean up other resources, save state to a disk or execute any other logic you might want when an object goes out of scope

Retain cycles and weak references

Key points

  • Class inheritance is one of the most important features of classes and enables polymorphism.

  • Subclassing is a powerful tool, but it’s good to know when to subclass. Subclass when you want to extend an object and could benefit from an “is-a” relationship between subclass and superclass, but be mindful of the inherited state and deep class hierarchies.

  • The keyword override makes it clear when you are overriding a method in a subclass.

  • The keyword final can be used to prevent a class from being subclassed.

  • Swift classes use two-phase initialization as a safety measure to ensure all stored properties are initialized before they are used.

  • Class instances have their own lifecycles which are controlled by their reference counts.

  • Automatic reference counting, or ARC, handles reference counting for you automatically, but it’s important to watch out for retain cycles.

Last updated