创建自己的类
类与结构相似,因为它们使您可以使用属性和方法创建新类型,但是它们有五个重要的区别,我将一次向您介绍所有这些区别。
类和结构之间的第一个区别是类永远不会带有成员初始化器。这意味着,如果您的类中有属性,则必须始终创建自己的初始化程序。
例如:
class Dog {
var name: String
var breed: String
init(name: String, breed: String) {
self.name = name
self.breed = breed
}
}
创建该类的实例看起来就像是一个结构一样:
let poppy = Dog(name: "Poppy", breed: "Poodle")
类继承
类和结构之间的第二个区别是,您可以基于现有类创建一个类-它继承了原始类的所有属性和方法,并且可以在顶部添加自己的类。
这称为类继承或子类,您从其继承的类称为“父”或“超级”类,新类称为“子”类。
这是Dog
我们刚刚创建的类:
class Dog {
var name: String
var breed: String
init(name: String, breed: String) {
self.name = name
self.breed = breed
}
}
我们可以基于一个名为的类创建一个新类Poodle
。默认情况下,它将继承Dog
相同的属性和初始化程序:
class Poodle: Dog {
}
但是,我们也可以提供Poodle
自己的初始化程序。我们知道它将始终具有品种“贵宾犬”,所以我们可以做一个新的初始化器,只需要一个name
属性。更好的是,我们可以让Poodle初始值设定项直接调用Dog初始值设定项,以便发生所有相同的设置::
class Poodle: Dog {
init(name: String) {
super.init(name: name, breed: "Poodle")
}
}
出于安全原因,Swift总是让您super.init()
从子类中进行调用-以防万一父类在创建时会做一些重要的工作。
覆盖方法
子类可以用其自己的实现替换父方法-这个过程称为Overriding。这是一个Dog
带有makeNoise()
方法的普通类:
class Dog {
func makeNoise() {
print("Woof!")
}
}
如果我们创建一个Poodle
继承自Dog
的新类,它将继承该makeNoise()
方法。因此,这将显示“ Woof!”:
class Poodle: Dog {
}
let poppy = Poodle()
poppy.makeNoise()
方法重写允许我们更改 Poodle
类的makeNoise()
实现。
Swift要求我们在重载方法时使用override func
而不是仅仅使用func
–它阻止您无意中重载方法,并且如果您尝试重载父类中不存在的内容,则会收到错误消息:
class Poodle: Dog {
override func makeNoise() {
print("Yip!")
}
}
进行此更改后,poppy.makeNoise()
将打印“ Yip!”而不是“ Woof!”。
Final类
尽管类继承非常有用-实际上苹果平台的大部分都需要您使用它-有时您还是想禁止其他开发人员根据您自己的类来构建他们的类。
Swift为此提供了一个关键字final
:当您将一个类声明为final
时,其他任何类都不能从该类继承。这意味着他们无法覆盖您的方法来更改您的行为–他们需要以编写类的方式使用您的类。
要使类最终定名,只需将final
关键字放在其前面,如下所示:
final class Dog {
var name: String
var breed: String
init(name: String, breed: String) {
self.name = name
self.breed = breed
}
}
复制对象
类和结构之间的第三个区别是如何复制它们。复制结构时,原始结构和复制结构都是不同的-更改一个不会更改另一个。复制类时,原始副本和复制副本都指向同一件事,因此更改一个同时也会更改另一个。
例如,这是一个简单的Singer
类,具有一个name
具有默认值的属性:
class Singer {
var name = "Taylor Swift"
}
如果创建该类的实例并打印其名称,则将得到“ Taylor Swift”:
var singer = Singer()
print(singer.name)
现在让我们从第一个变量创建第二个变量并更改其名称:
var singerCopy = singer
singerCopy.name = "Justin Bieber"
由于该方法的类的工作,既singer和singerCopy指向内存中的同一个对象,所以当我们打印歌手的名字,我们再次看到“Justin Bieber”:
print(singer.name)
另一方面,如果Singer
是一个结构,那么我们将第二次打印“ Taylor Swift”:
struct Singer {
var name = "Taylor Swift"
}
反初始化器
类和结构之间的第四个区别是,类可以具有反初始化器 -当类的实例被破坏时运行的代码。
为了说明这一点,这是一个Person
带有name
属性,简单的初始化程序和打印消息的printGreeting()
方法的类:
class Person {
var name = "John Doe"
init() {
print("\(name) is alive!")
}
func printGreeting() {
print("Hello, I'm \(name)")
}
}
我们将Person
在循环中创建该类的一些实例,因为每次循环进行时,都会创建一个新人员,然后将其销毁:
for _ in 1...3 {
let person = Person()
person.printGreeting()
}
现在是反初始化器。Person
实例销毁时将调用此方法:
deinit {
print("\(name) is no more!")
}
可变性
类和结构之间的最终区别是它们处理常量的方式。如果您具有带有可变属性的常量结构,则该属性不能更改,因为结构本身是常量。
但是,如果您的常量类具有可变属性,则可以更改该属性。因此,类不需要带有mutating
更改属性的方法的关键字。只有结构才需要。
这种差异意味着即使将类创建为常量,也可以更改类的任何变量属性–这是完全有效的代码:
class Singer {
var name = "Taylor Swift"
}
let taylor = Singer()
taylor.name = "Ed Sheeran"
print(taylor.name)
如果要阻止这种情况发生,则需要使该属性不变:
class Singer {
let name = "Taylor Swift"
}
总结
- 1.类和结构相似,它们都可以让您使用属性和方法创建自己的类型。
- 2.一个类可以从另一个类继承,并获得父类的所有属性和方法。谈论类层次结构是很常见的–一个类基于另一个类,而另一个类本身又基于另一个类。
- 3.您可以使用
final
关键字标记一个类,这将阻止其他类从该类继承。 - 4.通过方法覆盖,子类可以使用新的实现替换其父类中的方法。
- 5.当两个变量指向同一类实例时,它们都指向同一块内存–一个会改变另一个。
- 6.类可以具有一个反初始化器,该反初始化器是在销毁该类的实例时运行的代码。
- 7类并不像构造结构那样强烈地强制执行常量–如果将属性声明为变量,则无论如何创建类实例,都可以对其进行更改。