?????
1. 繼承的簡單介紹
- 值類型(枚舉舔亭、結(jié)構(gòu)體)不支持繼承些膨。只有類支持繼承
- 沒有父類的類,稱為:基類 分歇;
Swift
中沒有OC
傀蓉、Java
那樣的規(guī)定,所有類最終都要繼承自某個基類 - 子類可以重寫父類的
下標(biāo)
职抡、方法
葬燎、屬性
,重寫必須加上override
關(guān)鍵字
1.1 重寫實(shí)例方法缚甩、下標(biāo)
class Animal {
func speak() {
print("Animal Speak")
}
subscript(index: Int)-> Int{
get{
return index
}
}
}
class Dog: Animal {
override func speak() {
super.speak()
print("Dog Speak")
}
override subscript(index: Int) -> Int {
get{
return super[index] + 1
}
}
}
class ErHa: Dog {
var iq = 0
}
var ani = Animal()
var d = Dog()
ani.speak()
print("Ani:",ani[1])
print("================")
d.speak()
print("Dog:",d[1])
print("================")
打印結(jié)果:
Animal Speak
Ani: 1
================
Animal Speak
Dog Speak
Dog: 2
================
注意:
- 父類指針指向子類對象時谱净,就是多態(tài),在調(diào)用時擅威,調(diào)用的是指針指向?qū)ο蟮姆椒?/li>
- 被
class
修飾的類型方法
壕探、下標(biāo)
【允許
】被子類重寫 - 被
static
修飾的類型方法
、下標(biāo)
【不允許
】被子類重寫
1.2 重寫實(shí)例屬性
- 子類
可以
將父類的屬性(存儲郊丛、計(jì)算)
重寫為計(jì)算屬性
- 子類
不可以
將父類屬性
重寫為存儲屬性
- 子類只能重寫父類的
var
屬性李请,不能重寫let
屬性 - 重寫時,屬性名厉熟、類型要一致
- 子類重寫后的權(quán)限不能小于父類屬性的權(quán)限
- 如果父類是只讀的导盅,那么子類重寫后的屬性可以是只讀的、也可以是讀寫的
- 如果父類是讀寫的揍瑟,那么子類重寫后的屬性也必須是可讀寫的
實(shí)例:
class Circle {
var radius: Int = 0
var diameter: Int{
set{
print("Circle setDiameter")
radius = newValue / 2
}
get{
print("Circle getDiameter")
return radius * 2
}
}
}
class SubSircle: Circle {
override var radius: Int {
set{
print("SubSircle setRadius")
super.radius = newValue > 0 ? newValue : 0
}
get{
print("SubSircle setRadius")
return super.radius
}
}
override var diameter: Int {
set{
print("SubSircle setDiameter")
super.diameter = newValue > 0 ? newValue : 0
}
get{
print("SubSircle getDiameter")
return super.diameter
}
}
}
var circle = SubSircle()
circle.radius = 9
print("=======")
print(circle.diameter)
print("=======")
circle.diameter = 20
print("=======")
print(circle.radius)
【打印結(jié)果】
SubSircle setRadius
=======
SubSircle getDiameter
Circle getDiameter
SubSircle setRadius
18
=======
SubSircle setDiameter
Circle setDiameter
SubSircle setRadius
=======
SubSircle setRadius
10
1.2 重寫類型屬性
- 被
class
修飾的計(jì)算類型屬性
白翻,可以
被子類重寫(存儲類型屬性不可以被重寫) - 被
static
修飾的類型屬性(存儲、計(jì)算)
绢片,不可以
被子類重寫
2. 屬性觀察器
- 可以在子類中為父類屬性(除了只讀計(jì)算屬性滤馍、
let
屬性)增加屬性觀察器 - 父類的屬性在他自己的初始化器中賦值不會觸發(fā)屬性觀察器岛琼,但是在子類的初始化器中賦值會觸發(fā)屬性觀察器
3. 初始化器
- 類、結(jié)構(gòu)體巢株、枚舉都可以定義初始化器
- 類有兩種初始化器:
指定初始化器
槐瑞、便捷初始化器
- 每一個類至少要有一個指定初始化器,指定初始化器是類的主要初始化器
- 默認(rèn)初始化器總是類的指定初始化器
//指定初始化
class Person {
var name: String = ""
var age: Int = 0
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
//便捷初始化
class Student {
var name: String = ""
var age: Int = 0
convenience init(name: String, age: Int){
self.init()
self.name = name
self.age = age
}
}
var p = Person(name: "Jack", age: 18)
var s = Student()
var s2 = Student(name: "Jack", age: 18)
注意:
如果重新寫了指定初始化器纯续。則init()不再自動生成
3.1 初始化器的相互調(diào)用規(guī)則
- 指定初始化器必須從他的直系父類調(diào)用指定初始化器
- 便捷初始化器必須從相同的類里調(diào)用另一個初始化器
- 便捷初始化器最終必須調(diào)用一個指定初始化器
3.2 兩段式初始化
??Swift在變成方面為了保證初始化過程中的安全随珠,設(shè)定了兩段式初始化
、安全檢查
猬错。
第一階段:
初始化所有存儲屬性
- 外層調(diào)用指定、便捷初始化器
- 分配內(nèi)存給實(shí)例茸歧,但未初始化
- 指定初始化器確保當(dāng)前類定義的存儲屬性都初始化
- 指定初始化器調(diào)用父類的初始化器倦炒,不斷向上調(diào)用,形成初始化器鏈
第二階段:
設(shè)置新的存儲屬性
- 從頂部初始化器往下软瞎,鏈中的每一個指定初始化器都有機(jī)會進(jìn)一步制定實(shí)例
- 從初始化器現(xiàn)在能夠使用
self
(訪問逢唤、修改它的屬性,調(diào)用它的實(shí)例方法等等)- 最終涤浇,鏈中任何便捷初始化器都有機(jī)會定制實(shí)例以及使用
self
3.3 安全檢查
- 指定初始化器必須保證在調(diào)用父類初始化器之前鳖藕,其所在類定義的所有存儲屬性都要初始化完成
- 指定初始化器必須先調(diào)用父類初始化器,然后才能為集成的屬性設(shè)置新值
- 便捷初始化器必須先調(diào)用同類中的其他初始化器只锭,然后再為任意屬性設(shè)置新值
- 初始化器在第1階段初始化完成之前著恩,不能調(diào)用任何實(shí)例方法、不能讀取任何實(shí)例屬性的值蜻展,也不能引用
self
- 知道第一階段結(jié)束喉誊,實(shí)例才算完全合法
3.4 初始化器的重寫
- 當(dāng)重寫父類的指定初始化器時,必須加上 override (即使子類的實(shí)現(xiàn)是便捷式初始化器)
- 如果子類寫了一個匹配父類便捷初始化器的初始化器纵顾,不用加上override
- 因?yàn)楦割惖谋憬莩跏蓟饔肋h(yuǎn)不會通過子類直接調(diào)用伍茄,因此,嚴(yán)格來說施逾,子類無法重寫父類的便捷初始化器
3.3 自動繼承
- 1 如果子類沒有定義任何指定初始化器敷矫,他們會自動繼承父類所有的指定初始化器
- 2 如果子類提供了父類所有指定初始化器的實(shí)現(xiàn)(要么通過方式1繼承,要么重寫)汉额,子類會自動繼承所有的父類便捷初始化器
- 3 就算子類添加了更多的便捷初始化器曹仗,這些規(guī)則仍然適用
- 4 子類以便捷初始化器的形式重寫父類的指定初始化器,也可以作為滿足規(guī)則②的一部分
4. required關(guān)鍵字
- 用
required
關(guān)鍵字修飾指定初始化器闷愤,表明其所有子類都必須實(shí)現(xiàn)該初始化器(通過繼承或者重寫實(shí)現(xiàn)) - 如果子類重寫了
required
初始化器時整葡,也必須加上required
,不用加override
class Person {
required init(){
}
init(age: Int) {
}
}
class Student: Person {
required init() {
super.init()
}
}
4. 可失敗初始化器
- 類讥脐、結(jié)構(gòu)體遭居、枚舉都可以使用
init啼器?
定義可失敗初始化器 - 不允許同時定義參數(shù)標(biāo)簽、參數(shù)個數(shù)俱萍、參數(shù)類型相同的可失敗初始化器和非可失敗初始化器
- 可以用
init端壳!
定義隱式解包的可失敗初始化器 - 可失敗初始化器可以調(diào)用非可失敗初始化器,非可失敗初始化器調(diào)用可失敗初始化器需要進(jìn)行解包
- 如果初始化器調(diào)用了一個可失敗初始化器枪蘑,導(dǎo)致初始化失敗损谦,那么整個初始化過程都失敗,并且之后的代碼都停止執(zhí)行
- 可以用一個非可失敗初始化器重寫一個可失敗初始化器岳颇,但反過來是不行的
//非可失敗 調(diào)用 可失敗初始化器
class Person {
var name: String
var age: Int
init?(name:String) {
if name.isEmpty {
return nil
}else{
self.name = name
}
self.age = 18
}
convenience init() {
self.init(name:"Jack")!//
}
}
var p = Person()
print(p)
//可失敗 調(diào)用 非可失敗初始化器
class Person {
var name: String
var age: Int
convenience init?(name:String) {
self.init()
if name.isEmpty {
return nil
}
}
init() {
self.name = "jack"
self.age = 10
}
}
var p = Person(name: "")
print(p)
var p2 = Person(name: "Jack")
print(p2)
5. 反初始化器
deinit
叫做反初始化器照捡,類似于C++的析構(gòu)函數(shù)、OC中的dealloc
- 當(dāng)類的實(shí)例隊(duì)形被釋放內(nèi)存時话侧,就會調(diào)用實(shí)例隊(duì)形的
deinit
方法 - deinit不接受任何參數(shù)栗精,不能寫小括號,不能自行調(diào)用
- 父類的
deinit
能被子類繼承 - 子類的
deinit
實(shí)現(xiàn)執(zhí)行完畢后會調(diào)用父類的deinit
class Animal {
deinit {
print("Animal deinit")
}
}
class Dog:Animal {
deinit {
print("Dog deInit")
}
}
var dog:Dog? = Dog()
dog = nil
【打印結(jié)果】:
Dog deInit
Animal deinit
6. 可選鏈
class Car { var price = 100000 }
class Dog { var weight = 0 }
class Person {
var name: String = ""
var dog: Dog = Dog()
var car: Car? = Car()
func age() -> Int {
return 18
}
func eat() {
print("Person eat")
}
subscript(index: Int) -> Int{
return index
}
}
var person:Person? = Person()
var age1 = person!.age()// Int
var age2 = person?.age()// Int?
var index = person?[6]//Int?
if let age = person?.age(){
print(age)
}else{
print("age() 調(diào)用失敗")
}
- 如果可選項(xiàng)為nil瞻鹏,調(diào)用方法悲立、下標(biāo)、屬性失敗新博,結(jié)果為nil
- 如果可選項(xiàng)不為nil薪夕,調(diào)用方法、下標(biāo)赫悄、屬性成功原献,結(jié)果會被包裝成可選項(xiàng)
如果結(jié)果本身就是可選項(xiàng),則不會再次包裝 - 多個涩蜘?可以連在一起嚼贡,如果鏈中的任何一個節(jié)點(diǎn)為nil,那么整個鏈就會調(diào)用失敗
func getName() -> String {
return "Jack"
}
person?.name = getName()
注意
: 如果person為nil同诫,則不會調(diào)用getName()
ar num1: Int? = 5
num1? = 10//Optional(10)
var num2: Int? = nil
num2? = 10//nil
print(num1,num2)
var dict:[String:(Int, Int) -> Int] = [
"sum" : (+),
"diff":(-)
]
var result = dict["sum"]?(10,20)//Optional(30)