Swift之繼承

本文首發於我的博客html

前言

同其餘語言同樣,Swift中也是有繼承的git

  • 值類型(枚舉、結構體)不支持繼承,只有類支持繼承
  • 沒有父類的類,稱爲:基類
    • Swift並無像OC、Java那樣的規定:任何類最終都要繼承自某個基類
  • 子類能夠重寫父類的下標、方法、屬性,重寫必須加上override關鍵字

類繼承的內存結構

  • 有以下Animal類,其中Dog 類繼承Animal類 ,其中ErHa 類繼承Dog類
class Animal {
    var age = 0
}
class Dog : Animal {
    var weight = 0
}
class ErHa : Dog {
    var iq = 0
}
複製代碼

Animal類的內存

以下代碼查看類的內存大小和相應內存的值,須要用到工具Mems 關於的使用能夠看Swift之枚舉一文,或者直接看github上Mems的說明github

let a = Animal()
a.age = 5
print(Mems.size(ofRef: a))
print(Mems.memStr(ofRef: a))
複製代碼

Animal類的內存大小和內容,分別爲編程

32
0x00000001000084d8 //存放類的相關信息
0x0000000000000002 //引用技術
0x0000000000000005  // age數值5
0x0000000000000000	//沒用到
複製代碼

Animal類的內存分析

  • 首先類自己就要佔用16個字節,其中8個存放類的信息,8個存放引用技術,具體分析見Swift之類
  • 而後是 age的值5 佔用8個字節,總共24個字節,
  • 由於內存對齊的緣由,必須是16的倍數,因此須要32個字節,其中最後8個字節用不到,全是0

Dog類的內存

以下代碼查看類的內存大小和相應內存的值bash

let d = Dog()
d.age = 6
d.weight = 7
print(Mems.size(ofRef: d))
print(Mems.memStr(ofRef: d))
複製代碼

Dog類的內存大小和內容,分別爲app

32
0x0000000100008588 //存放類的相關信息
0x0000000000000002 //引用技術
0x0000000000000006  // age數值6
0x0000000000000007	//weight的值7
複製代碼

Dog類的內存分析

  • 首先類自己就要佔用16個字節,其中8個存放類的信息,8個存放引用技術,具體分析見Swift之類
  • 而後是 age的值6 佔用8個字節,weight的值7 佔用8個字節, 總共32個字節,
  • 由於內存對齊的緣由,必須是16的倍數,可是32正好是16的倍數,因此總共須要32個字

ErHa類的內存

以下代碼查看類的內存大小和相應內存的值ide

let e = ErHa()
e.age = 8
e.weight = 9
e.iq = 10
print(Mems.size(ofRef: e))
print(Mems.memStr(ofRef: e))
複製代碼

ErHa類的內存大小和內容,分別爲工具

48
0x0000000100008658 //存放類的相關信息
0x0000000000000002 //引用技術
0x0000000000000008  // age數值8
0x0000000000000009	//weight的值9
0x000000000000000a	//iq的值10
0x0000000000000000	//內存對齊增長的,沒用到
複製代碼

ErHa類的內存分析

  • 首先類自己就要佔用16個字節,其中8個存放類的信息,8個存放引用技術,具體分析見Swift之類
  • 而後是 age,weight,iq分別佔用8個字節,16+24 = 40個字節了
  • 由於內存對齊的緣由,必須是16的倍數,因此須要48個字節

重寫實例方法、下標

子類能夠重寫父類的實例方法、下標post

有個Animalui

class Animal {
    func speak() {
        print("Animal speak")
    }
    subscript(index: Int) -> Int {
        return index
    }
}
複製代碼

子類Cat繼承Animal,若是重寫示例方法,下標,必須用關鍵字override

class Animal {
    func speak() {
        print("Animal speak")
    }
    subscript(index: Int) -> Int {
        return index
    }
}
class Cat : Animal {
    override func speak() {
        super.speak()
        print("Cat speak")
    }
    override subscript(index: Int) -> Int {
        return super[index] + 1
    }
}
複製代碼

重寫類型方法、下標

上面的實例方法、下標。若是是類型方法、下標的話,有些許不一樣,

  • 被class修飾的類型方法、下標,容許被子類重寫
  • 被static修飾的類型方法、下標,不容許被子類重寫

eg

class Animal {
	//static修飾
    static func speak() {
        print("Animal speak")
    }
    // class修飾
    class subscript(index: Int) -> Int {
        return index
    }
}

print(Animal[6])
class Cat : Animal {
	//編譯報錯 Cannot override static method
    override class func speak() {
        super.speak()
        print("Cat speak")
    }
    //編譯成功
    override class subscript(index: Int) -> Int {
        return super[index] + 1
    }
}
複製代碼

如上面的代碼所示static修飾的時候,子類重寫,直接報錯Cannot override static method。而class修飾時候,編譯正常

重寫屬性

  • 子類能夠將父類的屬性(存儲、計算)重寫爲計算屬性
  • 子類不能夠將父類屬性重寫爲存儲屬性
  • 只能重寫var屬性,不能重寫let屬性
  • 重寫時,屬性名、類型要一致
  • 子類重寫後的屬性權限 不能小於 父類屬性的權限
    • 若是父類屬性是隻讀的,那麼子類重寫後的屬性能夠是隻讀的、也能夠是可讀寫的
    • 若是父類屬性是可讀寫的,那麼子類重寫後的屬性也必須是可讀寫的

重寫類型屬性

重寫類型屬性,比較簡單,不作贅述

重寫類型屬性

注意的是:若是子類把父類的存儲屬性int 重寫爲計算屬性,子類中依然有8個字節存儲該int屬性

再次須要注意的是,和重寫類型方法、下標相似 若是重寫類型屬性

  • 被class修飾的類型方法、下標,容許被子類重寫
  • 被static修飾的類型方法、下標,不容許被子類重寫

屬性觀察器

能夠在子類中爲父類屬性(除了只讀計算屬性、let屬性)增長屬性觀察器,依然是存儲屬性

eg,子類SubCircle給父類Circle的存儲屬性radius增長屬性觀察器

class Circle {
    var radius: Int = 1
}
class SubCircle : Circle {
    override var radius: Int {
        willSet {
            print("SubCircle willSetRadius", newValue)
        }
        didSet {
            print("SubCircle didSetRadius", oldValue, radius)
        }
    }
}

複製代碼

注意點:子類增長屬性觀察器以後,依然是存儲屬性

若是父類自己就有屬性觀察器

eg:

class Circle {
    var radius: Int = 1 {
        willSet {
            print("Circle willSetRadius", newValue)
        }
        didSet {
            print("Circle didSetRadius", oldValue, radius)
        }
    }
}
class SubCircle : Circle {
    override var radius: Int {
        willSet {
            print("SubCircle willSetRadius", newValue)
        }
        didSet {
            print("SubCircle didSetRadius", oldValue, radius)
        }
    }
}
var circle = SubCircle()

複製代碼

輸出爲

SubCircle willSetRadius 10
Circle willSetRadius 10
Circle didSetRadius 1 10
SubCircle didSetRadius 1 10
複製代碼

重寫父類的計算屬性

以下代碼

class Circle {
    var radius: Int {
        set {
            print("Circle setRadius", newValue)
        }
        get {
            print("Circle getRadius")
            return 20
        }
    }
}
class SubCircle : Circle {
    override var radius: Int {
        willSet {
            print("SubCircle willSetRadius", newValue)
        }
        didSet {
            print("SubCircle didSetRadius", oldValue, radius)
        }
    }
}

複製代碼

使用

var circle = SubCircle()
circle.radius = 10
複製代碼

輸出結果爲

Circle getRadius
SubCircle willSetRadius 10
Circle setRadius 10
Circle getRadius
SubCircle didSetRadius 20 20
複製代碼

輸出結果分析

  • 調用circle.radius = 10的時候,先獲取了oldValue,調用父類的get輸出Circle getRadius
  • 而後調用子類willSetRadius準備賦值
  • 調用父類setRadius
  • 準備調用子類的print("SubCircle didSetRadius", oldValue, radius)以前,要先獲取radius的值,因此,須要先執行父類的getRadius
  • 執行子類的didSet方法

final關鍵字

  • 被final修飾的方法、下標、屬性,禁止被重寫
  • 被final修飾的類,禁止被繼承

參考資料:

Swift官方源碼

從入門到精通Swift編程

相關文章
相關標籤/搜索