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