Class 类
类与结构体非常相似,我们都用它创建具有属性和方法的新数据类型。但是类引入了一个新的,重要而且复杂的功能-继承,使一个类建立在另一个类的基础上的能力。
继承是一个强大的功能,但是请记住要使代码保持简单:功能存在,但不意味着你需要使用它。
“任何傻瓜都可以编写计算机可以理解的代码,但是优秀的程序员可以编写人类可以理解的代码。”——Martin Fowler。
SwiftUI中广泛使用结构体,而数据中广泛使用了类。
与以前使用UIKit时,是非常不同的,UIKit中我们使用类进行UI设计,而使用结构体进行数据处理。
1.创建类
类与结构体的第一个区别就是,类中不会带有默认的成员初始化器。如果类中有属性,则必须创建自己的初始化方法。
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")
创建类的实例看起来和结构体一样。
为什么Swift要有类和结构体?
它们之间有五个重要的区别,后面我们详细解释这些差异。
1.类不带有合成的成员初始化器。
2.类可以通过继承,从而获得另一个类的属性和方法。
3.结构体的副本始终是唯一的,而类的副本实际上指向相同的共享数据。
4.类具有反初始化器,当类的实例被销毁时,会调用这些方法,而结构体不会。
5.常量类中的变量属性可以自由修改,而常量结构体中的变量不能随意修改。
2.类的继承
类与结构体的第二个区别,就是你可以基于现有类创建另一个类,它继承了原始类的所有属性和方法,并且可以添加自己的属性和方法。
这称为类的继承或者子类化,被继承的类称为"父类"或“超类”,新的类称为“子类”。
基于刚才我们创建的类,我们创建一个Poodle
类
class Poodle: Dog {
init(name: String) {
super.init(name: name, breed: "Poodle")
}
}
因为Poodle具有自己固定的品种,所有我们提供了一个仅需要name的初始化方法。
Swift总是要求在子类调用 super.init
方法,防止父类在创建时做一些重要工作。
为什么类不具有成员初始化器?
因为类有继承,使得成员初始化方法很难使用,当继承一个类后,然后在子类中添加了一些属性,此时父类的成员初始化方法在子类中将不再有用,所以,Swift让我自己提供成员初始化方法。不管添加任意数量的属性,都不影响类的继承。
3.重载 override
子类可以用自己的实现替换父类方法。
比如下面这个带有makeNoise()
方法的普通类。
class Dog {
func makeNoise() {
print("Woof!")
}
}
我们创建一个继承的新类。通过重载override func
父类方法。
class Poodle: Dog {
override func makeNoise() {
print("Yip!")
}
}
let poppy = Poodle()
poppy.makeNoise()
更改后,poppy.makeNoise()
将打印“Yip!”。
4.final class
如果你不想让其他类继承你的类,你可以将类声明为final,其它类将不能继承。
在类前添加final:
final class Dog {
var name: String
var breed: String
init(name: String, breed: String) {
self.name = name
self.breed = breed
}
}
final以前可以带来一点点的性能提升,更重要的是final让人更容易理解你的代码。
5. Copy
类与结构体的第三个区别是Copy。结构体拷贝后,原始和复制的结构体是不同的,改变一个不会更改其它的结构体,而类原始副本和复制副本指向了同一个事物,因此更改一个会更改另一个。
class Singer {
var name = "Taylor Swift"
}
var singer = Singer()
print(singer.name)
var singerCopy = singer
singerCopy.name = "Justin Bieber"
print(singer.name)
我们看到singer的名字已经变化。
如果改为结构体,两次将打印相同的结果。
为什么类要共享数据?
类的副本共享基础数据,结构体始终具有自身的唯一数据。
这种区别叫做“值类型与引用类型”。结构体是值类型,类时引用类型。
结构体的值万千包含在变量内,而引用类型复制时,会获得一个新的指针,指向原始对象所在的内存。
Swift人员更喜欢将结构体用于自定义类型,比如在一个大型程序中共享一个user对象时,如果使用的是类,有人把user数据改掉却不知道时,很可能会遇到问题。
有时候将要共享的东西,使用类改变时并通过一些方式发送消息给其他人,可能比拷贝一些对象更好一些。
6.Deinit
类和结构体之间的第四个区别是,类具有反初始化,在类被销毁时运行。
class Person {
var name = "John Doe"
init() {
print("\(name) is alive!")
}
func printGreeting() {
print("Hello, I'm \(name)")
}
deinit {
print("\(name) is no more!")
}
}
类为什么有deinit?
因为类的复制行为,很难判断类何时被销毁,Swift有”自动引用计数“的操作,ARC自动跟踪类的副本,当每次获得一个副本,Swift对其引用计数+1,每次被销毁时,Swift对其引用计数中-1,计数为0时,Swift调用反初始化程序并销毁该对象。
结构体有其自己的副本,不需要反初始化。
7.类的Mutability
类与结构体最终区别是,处理常量的方式。常量的结构体,属性不能更改,而类可以。
类不需要mutating关键字。
为什么可以更改常量类中的变量属性?
原因在于,一个指向的是内存中的某些数据,而另一个指向值。
结构体更改属性时,实际上是在更改整个结构。而类不会破坏并重新创建值。所以常量类可以自由更改变量属性。
8.总结
1.类和结构体类似,都可以使用属性和方法创建自己的类型。
2.一个类可以从另一类继承,并获得父类所有属性和方法。
3.final关键字标记一个类,阻止其它类继承。
4.通过重写,子类可以使用新的实现替换父类方法。
5.当两个变量指向同一个实例时,他们指向同一块内存。
6.类有deinit,销毁时运行。
7.将属性声明为变量时,无论类实例为常量或变量,都可以更改属性。
网友评论