- 值类型(枚举,结构体)不支持继承,只有类支持继承;
- 没有父类的类,称之为
基类
; - 子类可以重写父类的
下标
,方法
,属性
,重写时必须加上override
关键字;
内存结构
import Foundation
class Animal {
var age: Int = 0
}
class Dog: Animal {
var weight = 0
}
class ErHa: Dog {
var iq = 0
}
let a = Animal()
a.age = 10
let d = Dog()
d.age = 11
d.weight = 20
let eh = ErHa()
eh.age = 15
eh.weight = 30
eh.iq = 2
- 三种实例对象的内存结构如下:
重写实例方法,下标
import Foundation
class Animal {
var age: Int = 0
func speak() -> Void {
print("Animal speak")
}
subscript(index: Int) -> Int{
return index
}
}
class Dog: Animal {
var weight = 0
//重写实例方法
override func speak() {
super.speak()
print("Dog speak")
}
//重写下标
override subscript(index: Int) -> Int{
return super[index]+1
}
}
let a = Animal()
a.age = 10
a.speak() //Animal speak
print(a[6]) //6
let d = Dog()
d.age = 11
d.weight = 20
d.speak() //Animal speak Dog speak
print(d[6]) //7
重写类型方法,类型下标
- 被
class
修饰的类型方法,下标,允许被子类重写; - 被
static
修饰的类型方法,下标,不允许被子类重写;
class Animal {
var age: Int = 0
class func speak() -> Void {
print("Animal speak")
}
class subscript(index: Int) -> Int{
return index
}
}
class Dog: Animal {
var weight = 0
//重写类型方法
override class func speak() {
super.speak()
print("Dog speak")
}
//重写类型下标
override class subscript(index: Int) -> Int{
return super[index]+1
}
}
Animal.speak()
Animal[6]
Dog.speak()
Dog[6]
- 编译通过;
class Animal {
var age: Int = 0
static func speak() -> Void {
print("Animal speak")
}
static subscript(index: Int) -> Int{
return index
}
}
class Dog: Animal {
var weight = 0
override class func speak() {
super.speak()
print("Dog speak")
}
override class subscript(index: Int) -> Int{
return super[index]+1
}
}
Animal.speak()
Animal[6]
Dog.speak()
Dog[6]
- 出现报错,子类Dog不能重写父类Animal的类型方法,下标;
class Animal {
var age: Int = 0
class func speak() -> Void {
print("Animal speak")
}
class subscript(index: Int) -> Int{
return index
}
}
class Dog: Animal {
var weight = 0
override static func speak() {
super.speak()
print("Dog speak")
}
override static subscript(index: Int) -> Int{
return super[index]+1
}
}
Animal.speak()
Animal[6]
Dog.speak()
Dog[6]
- 编译通过;
重写属性
- 子类可以将父类的
属性
(存储,计算属性),重写为计算属性
; - 子类不可以将父类属性重写为存储属性;
-
只能重写var属性,不能重写let属性
; - 重写时,属性名,类型要保持一致;
- 子类重写后属性权限,
不能小于
父类的属性权限;- 如果父类的属性是只读的,那么子类重写后的属性可以是只读的,也可以是可读可写的;
- 如果父类的属性是可读可写的,那么子类重写后的属性必须是可读可写的;
重写实例属性
class Cirle {
var radius: Int = 0
var dimameter: Int {
set {
print("Cirle setDimameter")
radius = newValue / 2
}
get {
print("Cirle getDimameter")
return radius * 2
}
}
}
class SubCirle: Cirle{
//将存储属性 重写为 计算属性
override var radius: Int {
set {
print("SubCirle setDimameter")
super.radius = newValue > 0 ? newValue : 0
}
get {
print("subCirle getDimameter")
return super.radius
}
}
//重写计算属性
override var dimameter: Int {
set {
print("SubCirle setDimameter")
super.dimameter = newValue > 0 ? newValue : 0
}
get {
print("SubCirle getDimameter")
return super.dimameter
}
}
}
重写类型属性
- 被
class
修饰的类型属性,可以被子类重写
,且可以重写为static关键字修饰,保证不被下一级子类再重写; - 被
static
修饰的类型属性(存储,计算),不可以被子类重写
;
class Cirle {
static var radius: Int = 0
class var dimameter: Int {
set {
print("Cirle setDimameter")
radius = newValue / 2
}
get {
print("Cirle getDimameter")
return radius * 2
}
}
}
class SubCirle: Cirle{
override static var dimameter: Int {
set {
print("SubCirle setDimameter")
super.dimameter = newValue > 0 ? newValue : 0
}
get {
print("SubCirle getDimameter")
return super.dimameter
}
}
}
重写属性观察器
- 可以在子类中为父类的属性(除了只读计算属性,let属性),添加属性观察器;
class Cirle {
var radius: Int = 0
}
class SubCirle: Cirle{
override var radius: Int {
willSet {
print("SubCirle willSetRadius",newValue)
}
didSet {
print("SubCirle didSetRadius",oldValue,radius)
}
}
}
- 在SubCirle类中,给父类Cirle的
radius
属性,添加属性观察器;
class Cirle {
var radius: Int = 0 {
willSet {
print("Cirle willSetRadius",newValue)
}
didSet {
print("Cirle didSetRadius",oldValue,radius)
}
}
}
class SubCirle: Cirle{
override var radius: Int {
willSet {
print("SubCirle willSetRadius",newValue)
}
didSet {
print("SubCirle didSetRadius",oldValue,radius)
}
}
}
-
子类可以重写父类属性的属性观察器
;
final
- 被
final
修饰的方法,下标,属性,禁止被子类重写; - 被
final
修饰的类,禁止被继承;
多态的实现原理
- 针对结构体而言,编译完成,结构体实例调用的方法是确定的,因为它不存在继承,不存在重写;
struct Animal {
func speak() -> Void {
print("Animal speak")
}
func eat() -> Void {
print("Animal eat")
}
func sleep() -> Void {
print("Animal sleep")
}
}
var animal = Animal()
animal.speak()
animal.eat()
animal.sleep()
- 汇编代码如下:
- 将上面的结构体改成类,代码如下:
class Animal {
func speak() -> Void {
print("Animal speak")
}
func eat() -> Void {
print("Animal eat")
}
func sleep() -> Void {
print("Animal sleep")
}
}
var animal = Animal()
animal.speak()
animal.eat()
animal.sleep()
- 生成的汇编代码很多,与结构体的完全不同,这是因为
类的实例对象调用方法是不确定的,是动态的
,因为类存在继承,方法可重写,当前实例可能调用父类的方法,也能调用自己的方法;
animal.speak()
,对应的汇编callq *0x50(%rcx)
,其函数内存地址是动态的,不是写死的;将代码改成有继承关系的,如下所示:
class Animal {
func speak() -> Void {
print("Animal speak")
}
func eat() -> Void {
print("Animal eat")
}
func sleep() -> Void {
print("Animal sleep")
}
}
class Dog: Animal {
override func speak() -> Void {
print("Dog speak")
}
override func eat() -> Void {
print("Dog eat")
}
func run() -> Void {
print("Dog run")
}
}
var animal = Animal()
animal.speak()
animal.eat()
animal.sleep()
//父类指针指向子类对象
animal = Dog()
animal.speak()
animal.eat()
animal.sleep()
- 在
animal.speak()
所在行打下断点,汇编代码如下:
-
callq *0x50(%rcx)
就是animal.speak()
的汇编实现; - 首先
movq 0x4d5a(%rip), %rax
:(rip+ 0x4d5a)=0x1000082D8,就是全局变量animal的内存地址,其内存存储的是dog实例对象的内存地址0x0000000100529c40,然后将dog实例对象的内存地址写入rax寄存器; -
movq (%rax), %rcx
:(%rax)表示取rax中内存地址中的内容,按8个字节去取即dog的类型信息,然后存入rcx寄存器; -
callq *0x50(%rcx)
:*(rcx + 0x50)即取出这块的内存地址,也就是speak
函数地址,最后call进行调用; - 其调用原理如下图所示:
- 可以看出Dog的类型信息是存储在
全局区
,所有Dog实例对象共享这一份内存;
初始化器
- 类,结构体,枚举都可以定义初始化器;
- 类有两种初始化器:
指定初始化器
,便捷初始化器
; - 每个类至少有一个指定初始化器,指定初始化器是类的主要初始化器;
- 默认初始化器总是类的指定初始化器;
- 类偏向于少量指定初始化器,一个类通常只有一个指定初始化器;
- 初始化器相互调用规则:
- 指定初始化器必须从它的直系父类调用指定初始化器;
- 便捷初始化器必须从相同的类里调用另一个始化器;
- 便捷初始化器最终必须调用一个指定初始化器;
class Size {
var width: Int = 0
var height: Int = 0
//指定初始化器,是主要初始化器
init(width: Int,height: Int) {
self.width = width
self.height = height
}
//三个便捷初始化器
convenience init(width: Int){
self.init(width: width,height: 0)
}
convenience init(height: Int){
self.init(width: 0,height: height)
}
convenience init(){
self.init(width: 0,height: 0)
}
}
var s1 = Size()
var s2 = Size(width: 10)
var s3 = Size(height: 20)
var s4 = Size(width: 10, height: 20)
- 定义可一个指定初始化器,三个便捷初始化器,
便捷初始化器内部调用指定初始化器
,保证实例对象的所有存储属性
都有值;
class Person {
var age: Int
init(age: Int) {
self.age = age
}
convenience init(){
self.init(age: 0)
}
}
class Student : Person {
var score: Int
//指定指定初始化器
init(age: Int,score: Int) {
self.score = score
//调用父类的指定初始化器
super.init(age: age)
}
//便捷初始化器
convenience init(){
self.init(score: 0)
}
//便捷初始化器
//最终必须调用一个指定初始化器
convenience init(score: Int){
self.init(age: 0,score: score)
}
}
- Swift在编码安全方面是煞费苦心,为了保证初始化过程的安全,设置了
两段式初始化
,安全检查
两段式初始化
- 第一个阶段:初始化所有存储属性;
- 外层调用指定/便捷初始化器;
- 分配给实例,但未初始化;
- 指定初始化器必须要确保当前类定义的存储属性都初始化;
- 指定初始化器调用父类的指定初始化器,不断向上调用,形成初始化器链;
- 第二个阶段:设置新的存储属性值;
- 从顶部初始化器往下,链中的每一个指定初始化器都有机会进一步定制实例;
- 初始化器现在能使用
self
(访问,修改它的属性,调用它的实例方法等等) - 最终,链中任何便捷初始化器都有机会定制实例以及使用self;
class Person {
var age: Int
init(age: Int) {
self.age = age
//进入第二阶段 个性化定制
}
convenience init(){
self.init(age: 0)
}
}
class Student : Person {
var score: Int
//指定指定初始化器
init(age: Int,score: Int) {
self.score = score
//调用父类的指定初始化器
super.init(age: age)
//进入第二阶段 个性化定制
}
//便捷初始化器
convenience init(){
self.init(score: 0)
//进入第二阶段 个性化定制
}
//便捷初始化器
//最终必须调用一个指定初始化器
convenience init(score: Int){
self.init(age: 0,score: score)
//进入第二阶段 个性化定制
}
}
var stu = Student()
安全检查
- 指定初始化器必须保证在调用父类指定初始化器之前,其当前类定义的所有存储属性都要初始化完成;
- 指定初始化器必须先调用父类初始化器,然后才能为继承的属性设置新值;
- 便捷初始化器必须先调用同类中的其他初始化器,然后再为任意的属性设置新值;
- 初始化器在第一阶段初始化完成之前,不能调用任何实例方法,不能读取任何实例属性的值,也不能引用
self
- 直到第一阶段结束时,实例才算完全合法;
重写
- 当重写父类指定的初始化器时,必须加上
override
关键字(即使子类的实现是便捷初始化器)
class Person {
var age: Int
init(age: Int) {
self.age = age
}
}
class Student : Person {
var score: Int
//指定初始化器
init(age: Int,score: Int) {
self.score = score
//调用父类的指定初始化器
super.init(age: age)
}
//重写父类的指定初始化器
// override init(age: Int) {
// self.score = 0
// super.init(age: age)
// }
//将父类的指定初始化器 重写为便捷初始化器
override convenience init(age: Int) {
self.init(age: age,score: 0)
}
}
- 子类可以重写父类的指定初始化器需加上override关键字,
子类也可以将父类的指定初始化器,重写为便捷初始化器
同样要加上override关键字; -
子类是不能重写父类的便捷初始化器
的,便捷初始化器只能在当前类中横向调用,不能跨越在继承链上调用;
class Person {
var age: Int
//指定初始化器
init(age: Int) {
self.age = age
}
//便捷初始化器
convenience init(){
self.init(age: 0)
}
}
class Student : Person {
var score: Int
//指定初始化器
init(age: Int,score: Int) {
self.score = score
//调用父类的指定初始化器
super.init(age: age)
}
//不能加override
init() {
self.score = 0
super.init(age: 0)
}
}
-
Person
的便捷初始化器init()
, -
Student
的init()
,是不能加override的,因为Student是不能重写Person的便捷初始化器init();
自动继承
- 第一条:如果子类没有定义任何指定初始化器,它会自动继承父类的所有指定初始化器;
class Person {
var age: Int
var name: String
init(age: Int,name: String) {
self.age = age
self.name = name
}
init(age: Int){
self.age = age
self.name = ""
}
}
class Student : Person {
}
var stu1 = Student(age: 10)
var stu2 = Student(age: 10,name: "li")
- 第二条:如果子类拥有父类所有指定初始化器的实现(通过继承,重写的方式),那么子类会自动继承父类的所有便捷初始化器;
- 通过继承的方式就是啥都不干,如下所示:
class Person {
var age: Int
var name: String
init(age: Int,name: String) {
self.age = age
self.name = name
}
init(){
self.age = 0
self.name = ""
}
convenience init(age: Int) {
self.init(age: age,name: "")
}
convenience init(name: String) {
self.init(age: 0,name: name)
}
}
class Student : Person {
//自动继承父类所有指定初始化器
//继承父类所有便捷初始化器
}
var stu1 = Student(age: 10, name: "li")
var stu2 = Student()
var stu3 = Student(age: 10)
var stu4 = Student(name: "li")
- 通过重写的方式:
class Person {
var age: Int
var name: String
init(age: Int,name: String) {
self.age = age
self.name = name
}
init(){
self.age = 0
self.name = ""
}
convenience init(age: Int) {
self.init(age: age,name: "")
}
convenience init(name: String) {
self.init(age: 0,name: name)
}
}
class Student : Person {
//重写父类所有指定初始化器
override init(age: Int,name: String) {
super.init(age: age, name: name)
}
override init(){
super.init()
}
//继承父类所有便捷初始化器
}
var stu1 = Student(age: 10, name: "li")
var stu2 = Student()
var stu3 = Student(age: 10)
var stu4 = Student(name: "li")
- 第三条:若子类添加了更多的便捷初始化器,上述两条规则依然适用;
- 第四条:子类以便捷初始化器的方式也可重写父类的指定初始化器;
required
用
required
来修饰指定初始化器,表明其所有的子类必须实现该初始化器,子类可通过继承或者重写的方式来实现;如果子类重写了
required
初始化器,也必须加上required
,不用加override
通过继承的方式的来实现:啥都不干
class Person {
required init(){
}
init(age: Int) {
}
}
class Student : Person {
}
上述代码不会报错;
通过重写的方式的来实现:
class Person {
required init(){
}
init(age: Int) {
}
}
class Student : Person {
//自定义指定初始化器
init(no: Int) {
super.init(age: 0)
}
required init() {
super.init()
}
}
-
由于Student自定义了指定初始化器,那么父类所有指定初始化器就不能自动继承了
,所以Student需要手动去实现required init() ;
属性观察器
- 父类的属性在它自己的初始化器中赋值时,不会触发属性观察器,但在子类的初始化器中赋值会触发属性观察器;
class Person {
var age: Int {
willSet {
print("willSet",newValue)
}
didSet {
print("didSet",oldValue,age)
}
}
init() {
//不会触发属性观察器
self.age = 0
}
}
class Student : Person {
override init() {
super.init()
//触发属性观察器
self.age = 1
}
}
var stu = Student()
可失败初始化器
- 类,结构体,枚举都可以使用
init?
定义可失败初始化器;
class Person {
var name: String
//可失败初始化器
init?(name: String) {
if name.isEmpty {
return nil
}
self.name = name
}
}
var person = Person(name: "") //person是可选项类型
- 初始化器初始化实例可能出现失败,返回nil;
- person实例时可选项类型,即
Person?
- 不允许同时定义参数标签,参数个数,参数类型相同的可失败初始化器与非可失败初始化器;
class Person {
var name: String
init?(name: String) {
if name.isEmpty {
return nil
}
self.name = name
}
//报错 init?(name: String)与init(name: String) 不能同时存在
init(name: String) {
self.name = name
}
}
- 可以用
init!
定义隐式解包的可失败初始化器; - 可失败初始化器可调用非可失败初始化器;
class Person {
var name: String
//可失败初始化器
convenience init?(name: String) {
//可失败初始化器调用非可失败初始化器
self.init()
if name.isEmpty {
return nil
}
}
//非可失败初始化器
init() {
self.name = ""
}
}
- 非可失败初始化器调用可失败初始化器,需要进行解包;
class Person {
var name: String
//可失败初始化器
init?(name: String) {
if name.isEmpty {
return nil
}
self.name = name
}
//非可失败初始化器
convenience init() {
//非可失败初始化器调用可失败初始化器,需要进行解包
self.init(name: "")!
}
}
- 如果初始化器调用一个可失败初始化器导致初始化失败,那么整个初始化过程都失败,并且之后的代码中止执行;
class Person {
var name: String
//可失败初始化器
init?(name: String) {
if name.isEmpty {
return nil
}
self.name = name
}
//可失败初始化器
convenience init?() {
//调用可失败初始化器
self.init(name: "") //若失败 中止执行
self.name = "li"
}
}
- 可以用一个非可失败初始化器重写一个可失败初始化器,反之不行;
class Person {
var name: String
//可失败初始化器
init?(name: String) {
if name.isEmpty {
return nil
}
self.name = name
}
}
class Student : Person {
//重写成非可失败初始化器
override init(name: String) {
super.init(name: name)!
}
}
class Person {
var name: String
//非可失败初始化器
init(name: String) {
self.name = name
}
}
class Student : Person {
//重写成可失败初始化器
override init?(name: String) { //报错
super.init(name: name)
}
}
反初始化器
-
deinit
叫做反初始化器,类似于C++中的析构函数,OC中dealloc方法; - 当类的实例对象被释放内存时,就会调用实例对象的
deinit
方法; -
deinit
方法,不接收任何参数,不能写小括号,不能自行调用,由系统去调用; - 父类的
deinit
方法能被子类继承; - 子类的
deinit
方法调用完毕后,会去调用父类的deinit
方法;
class Person {
deinit {
print("Person对象销毁了")
}
}
class Student : Person {
deinit {
print("Student对象销毁了")
}
}
func test() -> Void {
var stu = Student()
}
print("-----")
test() //Student对象销毁了 -- Person对象销毁了
print("-----")
可选链
class Car {
var price: Int = 0
}
class Dog {
var weight: Int = 0
}
class Person {
var name: String = ""
var dog: Dog = Dog()
var car: Car? = Car()
func age() -> Int {
18
}
func eat() -> Void {
print("Person eat")
}
subscript(index: Int) -> Int{
index
}
}
var person: Person? = Person()
//如果person为空,不会调用age方法,直接返回nil
//如果person不为空,则调用age方法,返回age
var age = person?.age() //Int?
var name = person?.name //String?
var index = person?[6] //Int?
//person?.eat()的返回值是一个可选项类型,则可以使用可选项绑定
if let _ = person?.eat(){
print("eat 调用成功")
}else{
print("eat 调用失败")
}
var dog = person?.dog //Dog? 可选项
var weight = person?.dog.weight //Int? 可选项
var price = person?.
- 如果可选项为nil,那么调用方法,下标,属性都会失败,返回结果为nil;
- 如果可选项不为nil,那么调用方法,下标,属性会成功,返回结果会被包装成可选项,如果结果本来就是可选项,那么不会再次进行包装;
- 多个?可以链接在一起,组成可选链;
- 如果链中的任何一个节点为nil,那么整个链就会调用失败,返回结果为nil;
var scores = [
"Jack":[44,55,67],
"Rose":[23,59,89]
]
var arr = scores["jack"] //[Int]? 可选项
var s = scores["jack"]?[0]
var num1: Int? = 5
num1? = 10 //Optional(10)
var num2: Int? = nil
num2? = 10 //nil
var dic: [String : (Int,Int) -> Int] = [
"sum" : (+),
"diff" : (-)
]
var result = dic["sum"]?(10,20) //Optional(30)
-
scores
定义的是一个二维数组,scores["jack"]
是取出一个一维数组,如果key乱传,那么得到的结果就是nil,所以scores["jack"]
再根据下标去取元素时,需加上?,因为如果scores["jack"]
为nil,就不会去取元素了,最终得到的结果是一个可选项类型;