这是一个强大的功能,这是毫无疑问的,并且在开始构建真正的iOS应用程序时也没有避免使用类的方法。但是请记住要使代码简单:仅仅因为功能存在,并不意味着你需要使用它。正如马丁·福勒(Martin Fowler)所说,“任何傻瓜都可以编写计算机可以理解的代码,但是优秀的程序员可以编写人类可以理解的代码。”

创建类

类与结构相似,但是不自带初始化器,所以必须添加初始化相关的代码。

1
2
3
4
5
6
7
8
9
class Dog {
var name: String
var breed: String

init(name: String, breed: String) {
self.name = name
self.breed = breed
}
}

创建该类的实例看起来就像是一个结构一样:

1
let poppy = Dog(name: "Poppy", breed: "Poodle")

其他例子:

1
2
class Empty { }
let nothing = Empty()

类的继承

第二个与结构不同的是类具有继承能力。继承的类为子类,被继承为父类。例如我们有一个类:

1
2
3
4
5
6
7
8
9
class Dog {
var name: String
var breed: String

init(name: String, breed: String) {
self.name = name
self.breed = breed
}
}

这个类我们可以用一个子类来继承它,通过class <子类名称>: <父类名称>的形式继承。这个子类拥有父类所有的属性和初始化方法:

1
2
3
class Poodle: Dog {

}

有的时候我们还需要自定义子类的初始化参数,例如Poodle的类我们知道它的breed将始终为Poodle,所以我们给子类的初始化器传递数值:

1
2
3
4
5
class Poodle: Dog {
init(name: String) {
super.init(name: name, breed: "Poodle")
}
}

出于安全原因,Swift总是让你super.init()从子类进行调用-以防万一父类在创建时会做一些重要的工作。

其他例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Handbag {
var price: Int
init(price: Int) {
self.price = price
}
}
class DesignerHandbag: Handbag {
var brand: String
init(brand: String, price: Int) {
self.brand = brand
super.init(price: price)
}
}

覆盖方法

子类可以替换父类的方法(覆盖不会改变父类的方法,只是在子类中不使用父类方法并替换一个新的方法)例如:

1
2
3
4
5
class Dog {
func makeNoise() {
print("Woof!")
}
}

这是一个普通类,我们创建一个子类来继承它:

1
2
3
4
5
class Poodle: Dog {
}

let poppy = Poodle()
poppy.makeNoise()

我们可以将父类DogmakeNoise方法覆盖重写,我们可以将第二段的代码修改为:

1
2
3
4
5
6
7
8
class Poodle: Dog {
override func makeNoise() {
print("Yip!")
}
}

let poppy = Poodle()
poppy.makeNoise()

Swift要求我们在重载方法时使用override func而不是仅仅使用func。它阻止你无意中重载方法,并且如果你尝试重载父类中不存在的内容,则会收到错误消息:

进行此更改后,poppy.makeNoise()将打印“ Yip!”而不是“ Woof!”。

为了证明这个操作不会更改父类的方法,我对代码进行微小的调整:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Dog {
func makeNoise() {
print("Woof!")
}
}

class Poodle: Dog {
override func makeNoise() {
print("Yip!")
}
}

class Pood: Dog {
}

let poppy = Poodle()
let adr = Pood()


poppy.makeNoise()
adr.makeNoise()

禁止继承(最终类)

有的时候我们不想让其他开发人员来继承你的类,你可以使用final class来表达该类不可以被继承,例如:

1
2
3
4
5
6
7
final class Zhhoo {
var url: String
init (url: String) {
self.url = url
print("Let's go to the \(url)")
}
}

复制对象

和结构不同,类的直接复制(a = b,c = a这种形式)会让所有类都指向同一件事,他们之间互相干扰。而结构不会:

例如我们创建一个简单的类:

1
2
3
class Singer {
var name = "Taylor Swift"
}

如果创建该类的实例并打印其名称,则将得到“ Taylor Swift”:

1
2
var singer = Singer()
print(singer.name)

现在让我们创建一个新的变量,并且重新定义这个类中变量的名称:

1
2
var singerCopy = singer
singerCopy.name = "Justin Bieber"

由于该方法的类的工作,既singersingerCopy指向内存中的同一个对象,所以当我们打印歌手的名字,我们再次看到“贾斯汀比伯”:

1
print(singer.name)

另一方面,如果Singer是一个结构,那么我们将第二次打印“ Taylor Swift”:

1
2
3
struct Singer {
var name = "Taylor Swift"
}

例子:

在类中

a=b c=b print(a) != print(c)
a=b c=a d=a print(c) = print(d)
a=b c=a print(a) = print(c)

反初始化器

类与结构的不同,还有一点就是类支持反初始化器deinit。反初始化器可以在实例被销毁的时候执行。例如:

这是一个Person带有name属性,简单的初始化程序和printGreeting()打印消息的方法的类:

1
2
3
4
5
6
7
8
9
10
11
class Person {
var name = "John Doe"

init() {
print("\(name) is alive!")
}

func printGreeting() {
print("Hello, I'm \(name)")
}
}

我们将Person在循环中创建该类的一些实例,因为每次循环进行时,都会创建一个新人员,然后将其销毁:

1
2
3
4
for _ in 1...3 {
let person = Person()
person.printGreeting()
}

现在用于反初始化程序。Person实例销毁时将调用此方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Person {
var name = "John Doe"

init() {
print("\(name) is alive!")
}

func printGreeting() {
print("Hello, I'm \(name)")
}
//
deinit {
print("\(name) is no more!")
}
//
}

其他例子:

1
2
3
4
5
class IceCream {
deinit {
print("No more icecream :(")
}
}

可变性

类和结构之间的最终区别是它们处理常量的方式。如果你具有带有可变属性的常量结构,则该属性不能更改,因为结构本身是常量。

但是,如果你的常量类具有可变属性,则可以更改该属性。因此,类不需要mutating带有更改属性的方法的关键字。只有结构才需要。

这种差异意味着即使将类创建为常量,也可以更改类的任何变量属性–这是完全有效的代码:

1
2
3
4
5
6
7
class Singer {
var name = "Taylor Swift"
}

let taylor = Singer()
taylor.name = "Ed Sheeran"
print(taylor.name)

如果要阻止这种情况发生,则需要使该属性不变:

1
2
3
class Singer {
let name = "Taylor Swift"
}

其他例子:

1
2
3
4
5
6
7
8
class Pizza {
private var toppings = [String]()
func add(topping: String) {
toppings.append(topping)
}
}
var pizza = Pizza()
pizza.add(topping: "Mushrooms")

在结构struct中的例子:

1
2
3
4
5
6
7
8
struct Barbecue {
var charcoalBricks = 20
mutating func addBricks(_ number: Int) {
charcoalBricks += number
}
}
var barbecue = Barbecue()
barbecue.addBricks(4)

总结

  • 类和结构相似,它们都可以让你使用属性和方法创建自己的类型。
  • 一个类可以从另一个类继承,并获得父类的所有属性和方法。谈论类层次结构是很常见的–一个类基于另一个类,而另一个类本身又基于另一个类。
  • 你可以使用final关键字标记一个类,这将阻止其他类从该类继承。
  • 通过方法覆盖,子类可以使用新的实现替换其父类中的方法。
  • 当两个变量指向同一类实例时,它们都指向同一块内存–改变一个会改变另一个。
  • 类可以具有一个反初始化器,该反初始化器是在销毁该类的实例时运行的代码。
  • 类并不像构造结构那样强烈地强制执行常量–如果将属性声明为变量,则无论如何创建类实例,都可以对其进行更改。

参考资料

查看下一天的SwiftUI学习笔记

关于100days英文课程