20- 枚舉,枚舉原始值,枚舉相關(guān)值,switch提取枚舉關(guān)聯(lián)值
Swift枚舉:
Swift中的枚舉比OC中的枚舉強(qiáng)大,因?yàn)镾wift中的枚舉是一等類型,
它可以像類和結(jié)構(gòu)體一樣增加屬性和方法
格式:
enum Method{
case枚舉值
}
enumMethod{
case Add
case Sub
case Mul
case Div
可以連在一起寫
caseAdd, Sub, Mul, Div
}
可以使用枚舉類型變量或常量接收枚舉值
varm:Method= .Add
注意:如果變量或常量沒有指定類型,那么前面必須加上該值屬于哪個(gè)枚舉類型
varm1 =Method.Add
利用Switch匹配
注意:如果case中包含了所有的值,可以不寫default.
如果case中沒有包含枚舉中所有的值,必須寫default
switch(Method.Add){
caseMethod.Add:
print("加法")
caseMethod.Sub:
print("減法")
caseMethod.Mul:
print("除法")
caseMethod.Div:
print("乘法")
default:
print("都不是")
}
原始值:
OC中枚舉的本質(zhì)就是整數(shù),所以O(shè)C中的枚舉是有原始值的,默認(rèn)是從0開始
而Swift中的枚舉默認(rèn)是沒有原始值的,但是可以在定義時(shí)告訴系統(tǒng)讓枚舉有原始值
enum Method:枚舉值原始值類型{
case枚舉值
}
enumMethod2:Int{
可以連在一起寫
caseAdd, Sub, Mul, Div
}
和OC中的枚舉一樣,也可以指定原始值,后面的值默認(rèn)+1
enumMethod3:Int{
caseAdd =5, Sub, Mul, Div
}
Swift中的枚舉除了可以指定整形以外還可以指定其它類型,
但是如果指定其它類型,必須給所有枚舉值賦值,因?yàn)椴荒茏詣?dòng)遞增
enumMethod4:Double{
可以連在一起寫
caseAdd =5.0, Sub =6.0, Mul =6.1, Div =8.0
}
rawValue代表將枚舉值轉(zhuǎn)換為原始值,注意老版本中轉(zhuǎn)換為原始值的方法名叫toRaw
Method4.Sub.rawValue
原始值轉(zhuǎn)換為枚舉值
enumMethod5:String{
caseAdd ="add", Sub ="sub", Mul ="mul", Div ="div"
}
通過原始值創(chuàng)建枚舉值
注意:
1.原始值區(qū)分大小寫
2.返回的是一個(gè)可選值,因?yàn)樵贾祵?duì)應(yīng)的枚舉值不一定存在
3.老版本中為fromRaw("add")
letm2 =Method5(rawValue:"add")
print(m2)
funcchooseMethod(op:String)
{
由于返回是可選類型,所以有可能為nil,最好使用可選綁定
ifletopE =Method5(rawValue: op){
switch(opE){
case.Add:
print("加法")
case.Sub:
print("減法")
case.Mul:
print("除法")
case.Div:
print("乘法")
}
}
}
枚舉相關(guān)值:
可以讓枚舉值對(duì)應(yīng)的原始值不是唯一的,而是一個(gè)變量.
每一個(gè)枚舉可以是在某種模式下的一些特定值
enumlineSegmentDescriptor{
caseStartAndEndPattern(start:Double, end:Double)
caseStartAndLengthPattern(start: Double, length:Double)
}
varlsd =lineSegmentDescriptor.StartAndLengthPattern(start:0.0, length:100.0)
lsd=lineSegmentDescriptor.StartAndEndPattern(start:0.0, end:50.0)
利用switch提取枚舉關(guān)聯(lián)值
switchlsd
{
caselet.StartAndEndPattern(s, e):
print("start =\(s) end =\(e)")
case.StartAndLengthPattern(lets,letl):
print("start =\(s) lenght =\(l)")
}
21-結(jié)構(gòu)體,結(jié)構(gòu)體構(gòu)造器,定義成員方法
結(jié)構(gòu)體:
結(jié)構(gòu)體是用于封裝不同或相同類型的數(shù)據(jù)的
Swift中的結(jié)構(gòu)體是一類類型,可以定義屬性和方法(甚至構(gòu)造方法和析構(gòu)方法等)
格式:
struct結(jié)構(gòu)體名稱{
結(jié)構(gòu)體屬性和方法
}
structRect {
varwidth:Double=0.0
varheight:Double=0.0
}
如果結(jié)構(gòu)體的屬性有默認(rèn)值,可以直接使用()構(gòu)造一個(gè)結(jié)構(gòu)體
如果結(jié)構(gòu)體的屬性沒有默認(rèn)值,必須使用逐一構(gòu)造器實(shí)例化結(jié)構(gòu)體
varr =Rect()
print("width =\(r.width) height =\(r.height)")
輸出結(jié)果:width = 0.0 height = 0.0
結(jié)構(gòu)體屬性的訪問使用.語(yǔ)法
varr =Rect()
r.width=100
r.height=99
print("width =\(r.width) height =\(r.height)")
輸出結(jié)果:width = 100.0 height = 99.0
結(jié)構(gòu)體構(gòu)造器
Swift中的結(jié)構(gòu)體和類跟其它面向?qū)ο笳Z(yǔ)言一樣都有構(gòu)造函數(shù),而OC是沒有的
Swift要求實(shí)例化一個(gè)結(jié)構(gòu)體或類的時(shí)候,所有的成員變量都必須有初始值,
構(gòu)造函數(shù)的意義就是用于初始化所有成員變量的,而不是分配內(nèi)存,分配內(nèi)存是系統(tǒng)幫我們做的.
如果結(jié)構(gòu)體中的所有屬性都有默認(rèn)值,可以調(diào)用()構(gòu)造一個(gè)結(jié)構(gòu)體實(shí)例
如果結(jié)構(gòu)體中的屬性沒有默認(rèn)值,可以自定義構(gòu)造器,并在構(gòu)造器中給所有的屬性賦值
其實(shí)結(jié)構(gòu)體有一個(gè)默認(rèn)的逐一構(gòu)造器,用于在初始化時(shí)給所有屬性賦值
structRect2 {
varwidth:Double
varheight:Double=0.0
}
逐一構(gòu)造器
varr1 =Rect2(width:10.0, height:10.0);
錯(cuò)誤寫法,順序必須和結(jié)構(gòu)體中成員的順序一致
var r1 = Rect2(height: 10.0, width: 10.0);
錯(cuò)誤寫法,必須包含所有成員
var r1 = Rect2(height: 10.0);
結(jié)構(gòu)體中定義成員方法
在C和OC中結(jié)構(gòu)體只有屬性,而Swift中結(jié)構(gòu)體中還可以定義方法
structRect {
varwidth:Double
varheight:Double=0.0
給結(jié)構(gòu)體定義一個(gè)方法,該方法屬于該結(jié)構(gòu)體
結(jié)構(gòu)體中的成員方法必須使用某個(gè)實(shí)例調(diào)用
成員方法可以訪問成員屬性
funcgetWidth() ->Double{
returnwidth
}
}
varr =Rect(width:10.0, height:20.0)
結(jié)構(gòu)體中的成員方法是和某個(gè)實(shí)例對(duì)象綁定在一起的,
所以誰(shuí)調(diào)用,方法中訪問的屬性就屬于誰(shuí)
print(r.getWidth())
輸出結(jié)果:10.0
varr1 =Rect(width:30.0, height:20.0)
print(r1.getWidth())
輸出結(jié)果:30.0
結(jié)構(gòu)體是值類型
structRect {
varwidth:Double
varheight:Double=0.0
funcshow() ->Void{
print("width =\(width) height =\(height)")
}
}
varr1 =Rect(width:10.0, height:10.0)
varr2 =r1
r1.show()
r2.show()
r1.width=20.0
結(jié)構(gòu)體是值類型,結(jié)構(gòu)體之間的賦值其實(shí)是將r1中的值完全拷貝一份到r2中,
所以他們是兩個(gè)不同的實(shí)例
r1.show()
r2.show()
輸出結(jié)果:
width = 10.0 height = 10.0
width = 10.0 height = 10.0
width = 20.0 height = 10.0
width = 10.0 height = 10.0
22-類,類的恒等運(yùn)算
類的基本定義
Swift中的結(jié)構(gòu)體和類非常相似,但是又有不同之處
類是具有相同屬性和方法的抽象
格式:
class類名稱{
類的屬性和方法
}
classRect {
varwidth:Double=0.0
varheight:Double=0.0
funcshow() ->Void{
print("width =\(width) height =\(height)")
}
}
類沒有逐一構(gòu)造器
var r1 = Rect(width: 10.0, height: 10.0)
varr1 =Rect()
r1.show()
varr2 =r1
r2.show()
輸出結(jié)果:
width = 0.0 height = 0.0
width = 0.0 height = 0.0
類是引用類型,類之間的賦值其實(shí)是將r2指向了r1的存儲(chǔ)空間
所以他們是兩個(gè)只想同一塊存儲(chǔ)空間,修改其中一個(gè)會(huì)影響到另外一個(gè)
r1.width=99
r1.show()
r2.show()
輸出結(jié)果:
width = 99.0 height = 0.0
width = 99.0 height = 0.0
恒等運(yùn)算符
用于判斷是否是同一個(gè)實(shí)例,也就是是否指向同一塊存儲(chǔ)空間
=== !==
varr3 =Rect()
ifr1===r3
{
print("指向同一塊存儲(chǔ)空間")
}
輸出結(jié)果:
指向不同的存儲(chǔ)空間
23- 屬性,存儲(chǔ)屬性,延遲存儲(chǔ)屬性,計(jì)算屬性,屬性觀察器,類屬性
存儲(chǔ)屬性
Swift中的存儲(chǔ)屬性就是以前學(xué)習(xí)OC中的普通屬性
在結(jié)構(gòu)體或者類中定義的屬性,默認(rèn)就是存儲(chǔ)屬性
structPerson {
varname:String
varage:Int
}
varp:Person=Person(name:"lzh", age:30)
p.name="gxq"
p.age=50
常量存儲(chǔ)屬性
常量存儲(chǔ)屬性只能在定義時(shí)或構(gòu)造時(shí)修改
構(gòu)造好一個(gè)對(duì)象之后不能對(duì)常量存儲(chǔ)屬性進(jìn)行修改
structPerson {
varname:String
varage:Int
letcard:String身份證
}
varp:Person=Person(name:"gaowei", age:30, card:"123456")
p.name="gxq"
p.age=50
構(gòu)造好對(duì)象之后不能修改常量存儲(chǔ)屬性
以下寫法是錯(cuò)誤的
p.card = "56789"
結(jié)構(gòu)體和類常量與存儲(chǔ)屬性的關(guān)系
結(jié)構(gòu)體和枚舉是值類型
類是引用類型
structPerson {
varname:String
varage:Int
}
letp:Person=Person(name:"lzh", age:30)
因?yàn)榻Y(jié)構(gòu)體是值類型,所以不能修改結(jié)構(gòu)體常量中的屬性
不能修改結(jié)構(gòu)體/枚舉常量對(duì)象中的值,因?yàn)樗赶虻膶?duì)象是一個(gè)常量
以下寫法錯(cuò)誤
p.name =“gxq"
不能修改結(jié)構(gòu)體常量對(duì)象的值
以下寫法錯(cuò)誤
p=Person(name:"gxq", age:50)
classPerson {
varname:String="lnj"
varage:Int=30
}
letp:Person=Person()
可以修改類常量中的值,因?yàn)樗赶虻膶?duì)象不是一個(gè)常量
p.name=“l(fā)zh"
不可以修改類常量的指向
以下寫法是錯(cuò)誤的
p = Person4()
延遲存儲(chǔ)屬性
Swift語(yǔ)言中所有的存儲(chǔ)屬性必須有初始值,
也就是當(dāng)構(gòu)造完一個(gè)對(duì)象后,對(duì)象中所有的存儲(chǔ)屬性必須有初始值
但是也有例外
其中延遲存儲(chǔ)屬性可以將屬性的初始化推遲到該屬性第一次被調(diào)用的時(shí)候
懶加載應(yīng)用場(chǎng)景:
1.有可能不會(huì)用到
2.依賴于其它值
classLine {
varstart:Double=0.0
varend:Double=0.0
如果不是lazy屬性,定義的時(shí)候?qū)ο筮€沒有初始化,所以不能訪問self
如果加上lazy,代表使用時(shí)才會(huì)加載,也就是使用到length屬性時(shí)才會(huì)調(diào)用self
而訪問一個(gè)類的屬性必須通過對(duì)象方法
所以訪問時(shí)對(duì)象已經(jīng)初始化完成了,可以使用self
lazyvarlength:Double=self.getLenght()
通過閉包懶加載
lazyvarcontainer:Array = {
print("懶加載")
vararrM = []
returnarrMas[AnyObject]
}()
funcgetLenght() ->Double
{
print("懶加載")
returnend-start
}
}
varline =Line()
line.end=150.0
print("創(chuàng)建對(duì)象完畢")
print(line.length)
vararrM =line.container
arrM.append("1")
arrM.append(5)
print(arrM)
輸出結(jié)果:
創(chuàng)建對(duì)象完畢
懶加載
150.0
懶加載
[1, 5]
計(jì)算屬性
1.Swift中的計(jì)算屬性不直接存儲(chǔ)值
跟存儲(chǔ)屬性不同,沒有任何的"后端存儲(chǔ)與之對(duì)應(yīng)"
2.計(jì)算屬性用于計(jì)算,可以實(shí)現(xiàn)setter和getter這兩種計(jì)算方法
3.枚舉不可以有存儲(chǔ)屬性,但是允許有計(jì)算屬性
setter對(duì)象.屬性=值
getter var value =對(duì)象.屬性
structRect {
varorigion: (x:Double, y:Double) = (0,0)
varsize: (w:Double, h:Double) = (0,0)
由于center的值是通過起點(diǎn)和寬高計(jì)算出來(lái)的,所以沒有必要提供一個(gè)存儲(chǔ)屬性
varcenter: (x:Double, y:Double) {
get{
return(origion.x +size.w/2,origion.y +size.h/2)
}
set{
注意:計(jì)算屬性不具備存儲(chǔ)功能,所以不能給計(jì)算屬性賦值
如果賦值會(huì)發(fā)生運(yùn)行時(shí)錯(cuò)誤
注意: setter可以自己傳遞一個(gè)參數(shù),也可以使用系統(tǒng)默認(rèn)的參數(shù)newValue
如果要使用系統(tǒng)自帶的參數(shù),必須刪除自定義參數(shù)
origion.x = newValue.x -size.w /2
origion.y = newValue.y -size.h /2
}
}
}
varr =Rect()
r.origion= (0,0)
r.size= (100,100)
print("center.x =\(r.center.x) center.y =\(r.center.y)")
輸出結(jié)果:center.x = 50.0 center.y = 50.0
r.center= (100,100)
print("origion.x =\(r.origion.x) origion.y =\(r.origion.y)")
輸出結(jié)果:origion.x = 50.0 origion.y = 50.0
print("center.x =\(r.center.x) center.y =\(r.center.y)")
輸出結(jié)果:center.x = 100.0 center.y = 100.0
只讀計(jì)算屬性
對(duì)應(yīng)OC中的readonly屬性
所謂的只讀屬性就是只提供了getter方法,沒有提供setter方法
classLine {
varstart:Double=0.0
varend:Double=0.0
只讀屬性,只讀屬性必須是變量var,不能是常量let
例如想獲取長(zhǎng)度
只能通過計(jì)算獲得,而不需要外界設(shè)置,可以設(shè)置為只讀計(jì)算屬性
varlength:Double{
只讀屬性的簡(jiǎn)寫,可以省略get{}
returnend-start
}
}
varline =Line()
line.end=100
print(line.length)
輸出結(jié)果:100.0
屬性觀察器
類似OC中的KVO
可以用于監(jiān)聽屬性什么時(shí)候被修改,只有屬性被修改才會(huì)調(diào)用
有兩種屬性觀察器:
1.willSet,在設(shè)置新值之前調(diào)用
2.didSet,在設(shè)置新值之后調(diào)用
可以直接為除計(jì)算屬性和lazy屬性之外的存儲(chǔ)屬性添加屬性觀察器
但是可以在繼承類中為父類的計(jì)算屬性提供屬性觀察器
因?yàn)樵谟?jì)算屬性中也可以監(jiān)聽到屬性的改變
所以給計(jì)算屬性添加屬性觀察器沒有任何意義
classLine {
varstart:Double=0.0{
willSet{
print("willSet newValue =\(newValue)")
}
didSet{
print("didSet oldValue =\(oldValue)")
}
}
varend:Double=0.0
}
varl =Line()
l.start=10.0
輸出結(jié)果:
willSet newValue = 10.0
didSet oldValue = 0.0
類屬性
在結(jié)構(gòu)體和枚舉中用static
在類中使用class,并且類中不允許將存儲(chǔ)屬性設(shè)置為類屬性
structPerson {
普通的屬性是每個(gè)對(duì)象一份
varname:String=“l(fā)zh"
類屬性是素有對(duì)象共用一份
staticvargender:String="man"
staticvarage:Int{
return30
}
funcshow()
{
print("gender =\(Person.gender) name =\(name)")
}
}
varp =Person()
print("gender =\(Person.gender)")
輸出結(jié)果:gender = man
varp1 =Person()
類屬性是所有對(duì)象共用一份
print("gender =\(Person.gender)")
p.show()
輸出結(jié)果:
gender = man
gender = man name = lzh
可以將計(jì)算屬性設(shè)置為類屬性
print("age =\(Person.age)")
輸出結(jié)果:age = 30
classPerson {
普通的屬性是每個(gè)對(duì)象一份
varname:String="lnj"
類中不允許將存儲(chǔ)屬性定義為類屬性
下面為錯(cuò)誤寫法
class var gender:String = "man"
類中只能將計(jì)算屬性定義為類屬性
classvarage:Int{
return30
}
funcshow()
{
print("age =\(Person.age)")
}
}
varp =Person()
print("age =\(Person.age)")
p.show()
輸出結(jié)果:
age = 30
age = 30
24-方法,self關(guān)鍵字,mutating方法,類方法
方法
隸屬于每一個(gè)類或結(jié)構(gòu)體的函數(shù)稱之為方法:
方法分為類方法和實(shí)例方法,對(duì)應(yīng)OC中的+ -方法
實(shí)例方法:實(shí)例方法一定是通過對(duì)象來(lái)調(diào)用的,實(shí)例方法隸屬于某一個(gè)類
classPerson {
var_name:String="lzh"
var_age:Int=30
實(shí)例方法一定是通過對(duì)象來(lái)調(diào)用的,實(shí)例方法隸屬于某一個(gè)類
funcsetName(name:String,age:Int)
如果不希望某個(gè)參數(shù)作為外部參數(shù),可以在參數(shù)前面加上_,忽略外部參數(shù)
funcsetName(name:String,_age:Int)
{
_name= name
_age= age
}
funcshow()
{
print("name =\(_name) age =\(_age)")
}
}
varp =Person()
由于第一個(gè)參數(shù)可以通過方法名稱指定,所以默認(rèn)第一個(gè)參數(shù)不作為外部參數(shù)
p.setName("zs", age: 88)
可以在參數(shù)前面加上_,忽略外部參數(shù)
p.setName("zs",88)
p.show()
輸出結(jié)果:name = zs age = 88
self關(guān)鍵字
Swift中的self和OC中的self基本一樣. self指當(dāng)前對(duì)象
如果self在對(duì)象方法中代表當(dāng)前對(duì)象.但是在類方法中沒有self
classPerson {
varname:String=“l(fā)zh"
varage:Int=30
當(dāng)參數(shù)名稱和屬性名稱一模一樣時(shí),
無(wú)法區(qū)分哪個(gè)是參數(shù)哪個(gè)是屬性
這個(gè)時(shí)候可以通過self明確的來(lái)區(qū)分參數(shù)和屬性
funcsetName(name:String, age:Int)
{
默認(rèn)情況下, _name和_age前面有一個(gè)默認(rèn)的self關(guān)鍵字,
因?yàn)樗凶兞慷夹枰榷x再使用
而setName方法中并沒有定義過_name和_age,
而是在屬性中定義的,所以setName中訪問的其實(shí)是屬性,
編譯器默認(rèn)幫我們?cè)谇懊婕恿艘粋€(gè)self.
_name = name
_age = age
self.name= name
self.age= age
}
funcshow()
{
print("name =\(name) age =\(age)")
}
}
varp =Person()
p.setName("xq", age:20)
p.show()
輸出結(jié)果:name = xq age = 20
mutating方法
值類型(結(jié)構(gòu)體和枚舉)默認(rèn)方法是不可以修改屬性的
如果需要修改屬性
需要在方法前加上mutating關(guān)鍵字,讓該方法變?yōu)橐粋€(gè)改變方法
structPerson {
varname:String="lzh"
varage:Int=30
值類型(結(jié)構(gòu)體和枚舉)默認(rèn)方法是不可以修改屬性的
如果需要修改屬性
需要在方法前加上mutating關(guān)鍵字,讓該方法變?yōu)橐粋€(gè)改變方法
注意:類不需要,因?yàn)轭惖膶?shí)例方法默認(rèn)就可以修改
mutatingfuncsetName(name:String, age:Int)
{
self.name= name
self.age= age
}
funcshow()
{
print("name =\(name) age =\(age)")
}
}
varp =Person()
p.setName("zs", age:99)
p.show()
輸出結(jié)果:name = zs age = 99
enumLightSwitch{
caseOFF, ON
mutatingfuncnext()
{
switchself{
caseOFF:
self=ON
caseON:
self=OFF
}
}
}
varls:LightSwitch=LightSwitch.OFF
ifls==LightSwitch.OFF
{
print("off")
}
ls.next()
ifls==LightSwitch.ON
{
print("on")
}
輸出結(jié)果:
off
on
類方法:
和類屬性一樣通過類名來(lái)調(diào)用
類方法通過static關(guān)鍵字(結(jié)構(gòu)體/枚舉), class(類)
類方法中不存在self
structPerson {
varname:String=“l(fā)zh"
staticvarcard:String="123456"
funcshow()
{
print("name =\(self.name) card =\(Person.card)")
}
staticfuncstaticShow()
{
類方法中沒有self
print("name = \(self.name) card = \(Person.card)")
靜態(tài)方法對(duì)應(yīng)OC中的+號(hào)方法,和OC一樣在類方法中不能訪問非靜態(tài)屬性
print("card =\(Person.card)")
}
}
varp =Person()
p.show()
Person.staticShow()
輸出結(jié)果:
name = lzh card = 123456
card = 123456
classPerson {
varname:String="lzh"
classvarcard:String{
return"123456"
}
funcshow()
{
print("name =\(self.name) card =\(Person.card)")
}
classfuncstaticShow()
{
類方法中沒有self
print("name = \(self.name) card = \(Person.card)")
靜態(tài)方法對(duì)應(yīng)OC中的+號(hào)方法,和OC一樣在類方法中不能訪問非靜態(tài)屬性
print("card =\(Person.card)")
}
}
varp =Person()
p.show()
Person.staticShow()
輸出結(jié)果:
name = lzh card = 123456
card = 123456
25- 下標(biāo)subscripts
subscripts(下標(biāo)):
訪問對(duì)象中數(shù)據(jù)的快捷方式
所謂下標(biāo)腳本語(yǔ)法就是能夠通過,實(shí)例[索引值]來(lái)訪問實(shí)例中的數(shù)據(jù)
類似于以前我們?cè)L問數(shù)字和字典,其實(shí)Swift中的數(shù)組和字典就是一個(gè)結(jié)構(gòu)體
structStudent {
varname:String="lzh"
varmath:Double=99.0
varchinese:Double=99.0
varenglish:Double=99.0
funcscore(course:String) ->Double?
{
switchcourse{
case"math":
returnmath
case"chinese":
returnchinese
case"english":
returnenglish
default:
returnnil
}
}
要想實(shí)現(xiàn)下標(biāo)訪問,必須實(shí)現(xiàn)subscript方法
如果想要通過下標(biāo)訪問,必須實(shí)現(xiàn)get方法
如果想要通過下表賦值,必須實(shí)現(xiàn)set方法
subscript(course:String) ->Double?{
get{
switchcourse{
case"math":
returnmath
case"chinese":
returnchinese
case"english":
returnenglish
default:
returnnil
}
}
set{
switchcourse{
case"math":
因?yàn)榉祷氐氖强蛇x類型
math= newValue!
case"chinese":
chinese= newValue!
case"english":
english= newValue!
default:
print("not found")
}
}
}
}
varstu =Student(name:"zs",
math:99.0,
chinese:88.0,
english:10.0)
print(stu.score("math"))
輸出結(jié)果:Optional(99.0)
stu["chinese"]=100.0
print(stu["chinese"])
輸出結(jié)果:Optional(100.0)
26-繼承,super關(guān)鍵字,override關(guān)鍵字,final關(guān)鍵字
繼承語(yǔ)法
繼承是面向?qū)ο笞铒@著的一個(gè)特性,繼承是從已經(jīng)有的類中派生出新的類
新的類能夠繼承已有類的屬性和方法,并能擴(kuò)展新的能力
術(shù)語(yǔ):基類(父類,超類),派生類(子類,繼承類)
語(yǔ)法:
class子類:父類{
}
繼承優(yōu)點(diǎn):代碼重用
繼承缺點(diǎn):增加程序耦合度,父類改變會(huì)影響子類
注意:Swift和OC一樣沒有多繼承
classMan {
varname:String="lzh"
varage:Int=10
funcsleep(){
print("睡覺")
}
}
classSuperMan:Man{
varpower:Int=100
funsfly(){
子類可以繼承父類的屬性
print("飛\(name)\(age)")
}
}
varm =Man()
m.sleep()
父類不可以使用子類的方法
m.fly()
輸出結(jié)果:睡覺
varsm =SuperMan()
子類可以繼承父類的方法
sm.sleep()
sm.fly()
輸出結(jié)果:
睡覺
飛lzh 10
super關(guān)鍵字:
派生類中可以通過super關(guān)鍵字來(lái)引用父類的屬性和方法
classMan {
varname:String="lzh"
varage:Int=30
funcsleep(){
print("睡覺")
}
}
classSuperMan:Man{
varpower:Int=100
funceat()
{
print("吃飯")
}
funcfly(){
子類可以繼承父類的屬性
print("飛\(super.name)\(super.age)")
}
funceatAndSleep()
{
eat()
super.sleep()
如果沒有寫super,那么會(huì)先在當(dāng)前類中查找,如果找不到再去父類中查找
如果寫了super,會(huì)直接去父類中查找
}
}
varsm =SuperMan()
sm.eatAndSleep()
輸出結(jié)果:
吃飯
睡覺
方法重寫: override
重寫父類方法,必須加上override關(guān)鍵字
classMan {
varname:String="lzh"
varage:Int=10
funcsleep(){
print("父類睡覺")
}
}
classSuperMan:Man{
varpower:Int=100
override關(guān)鍵字主要是為了明確表示重寫父類方法,
所以如果要重寫父類方法,必須加上override關(guān)鍵字
overridefuncsleep() {
sleep()不能這樣寫,會(huì)導(dǎo)致遞歸
super.sleep()
print("子類睡覺")
}
funceat()
{
print("吃飯")
}
funcfly(){
子類可以繼承父類的屬性
print("飛\(super.name)\(super.age)")
}
funceatAndSleep()
{
eat()
sleep()
}
}
varsm =SuperMan()
通過子類調(diào)用,優(yōu)先調(diào)用子類重寫的方法
sm.sleep()
輸出結(jié)果:
父類睡覺
子類睡覺
sm.eatAndSleep()
輸出結(jié)果:
吃飯
父類睡覺
子類睡覺
重寫屬性
無(wú)論是存儲(chǔ)屬性還是計(jì)算屬性,都只能重寫為計(jì)算屬性
classMan {
varname:String="lzh"存儲(chǔ)屬性
varage:Int{計(jì)算屬性
get{
return30
}
set{
print("man new age\(newValue)")
}
}
funcsleep(){
print("睡覺")
}
}
classSuperMan:Man{
varpower:Int=100
可以將父類的存儲(chǔ)屬性重寫為計(jì)算屬性
但不可以將父類的存儲(chǔ)屬性又重寫為存儲(chǔ)屬性,因?yàn)檫@樣沒有意義
override var name:String = "zs"
overridevarname:String{
get{
return"zs"
}
set{
print("SuperMan new name\(newValue)")
}
}
可以將父類的計(jì)算屬性重寫為計(jì)算屬性,同樣不能重寫為存儲(chǔ)屬性
overridevarage:Int{計(jì)算屬性
get{
return30
}
set{
print("superMan new age\(newValue)")
}
}
}
letsm =SuperMan()
通過子類對(duì)象來(lái)調(diào)用重寫的屬性或者方法,肯定會(huì)調(diào)用子類中重寫的版本
sm.name="xxx"
sm.age=50
輸出結(jié)果:
SuperMan new name xxx
superMan new age 50
重寫屬性的限制
1.讀寫計(jì)算屬性/存儲(chǔ)屬性,是否可以重寫為只讀計(jì)算屬性? (權(quán)限變小)不可以
2.只讀計(jì)算屬性,是否可以在重寫時(shí)變成讀寫計(jì)算屬性? (權(quán)限變大)可以
classMan {
varname:String="lzh"存儲(chǔ)屬性
varage:Int{計(jì)算屬性
get{
return30
}
set{
print("man new age\(newValue)")
}
}
funcsleep(){
print("睡覺")
}
}
classSuperMan:Man{
varpower:Int=100
overridevarname:String{
get{
return"zs"
}
set{
print("SuperMan new name\(newValue)")
}
}
overridevarage:Int{計(jì)算屬性
get{
return30
}
set{
print("superMan new age\(newValue)")
}
}
}
重寫屬性觀察器
只能給非lazy屬性的變量存儲(chǔ)屬性設(shè)定屬性觀察器,
不能給計(jì)算屬性設(shè)置屬性觀察器,給計(jì)算屬性設(shè)置屬性觀察器沒有意義
屬性觀察器限制:
1.不能在子類中重寫父類只讀的存儲(chǔ)屬性
2.不能給lazy的屬性設(shè)置屬性觀察器
classMan {
varname:String="lzh"
varage:Int=0{存儲(chǔ)屬性
willSet{
print("super new\(newValue)")
}
didSet{
print("super new\(oldValue)")
}
}
varheight:Double{
get{
print("super get")
return10.0
}
set{
print("super set")
}
}
}
classSuperMan:Man{
可以在子類中重寫父類的存儲(chǔ)屬性為屬性觀察器
overridevarname:String{
willSet{
print("new\(newValue)")
}
didSet{
print("old\(oldValue)")
}
}
可以在子類中重寫父類的屬性觀察器
overridevarage:Int{
willSet{
print("child new\(newValue)")
}
didSet{
print("child old\(oldValue)")
}
}
可以在子類重寫父類的計(jì)算屬性為屬性觀察器
overridevarheight:Double{
willSet{
print("child height")
}
didSet{
print("child height")
}
}
}
varm =SuperMan()
m.age=55
輸出結(jié)果:
child new 55
super new 55
super new 0
child old 0
print(m.age)
輸出結(jié)果:55
m.height=20.0
輸出結(jié)果:
super get
child height
super set
child height
final關(guān)鍵字
利用final關(guān)鍵字防止重寫
final關(guān)鍵字既可以修飾屬性,也可以修飾方法,并且還可以修飾類
被final關(guān)鍵字修飾的屬性和方法不能被重寫
被final關(guān)鍵字修飾的類不能被繼承
finalclassMan {
finalvarname:String="lzh"
finalvarage:Int=0{//存儲(chǔ)屬性
willSet{
print("super new\(newValue)")
}
didSet{
print("super new\(oldValue)")
}
}
finalvarheight:Double{
get{
print("super get")
return10.0
}
set{
print("super set")
}
}
finalfunceat(){
print("吃飯")
}
}
27- 構(gòu)造方法,帶參數(shù)的構(gòu)造方法,常量存儲(chǔ)屬性與構(gòu)造方法,結(jié)構(gòu)體構(gòu)造方法
構(gòu)造方法
作用:對(duì)實(shí)例對(duì)象的內(nèi)容進(jìn)行初始化
Swift要求類或者結(jié)構(gòu)體中的存儲(chǔ)屬性(非lazy的)在對(duì)象構(gòu)造完畢后要有初始化值
語(yǔ)法:
init(參數(shù)列表){初始化代碼}
注意:
1.在Swift中類/結(jié)構(gòu)體/枚舉都需要構(gòu)造方法
2.構(gòu)造方法的作用僅僅是用于初始化屬性,而不是分配內(nèi)容,分配內(nèi)存是系統(tǒng)幫我們做的
3.構(gòu)造方法是隱式調(diào)用的,通過類名稱()形式創(chuàng)建一個(gè)對(duì)象就會(huì)隱式調(diào)用init()構(gòu)造方法
4.如果所有的存儲(chǔ)屬性都有默認(rèn)值,可以不提供構(gòu)造方法,系統(tǒng)會(huì)提供一個(gè)隱式的構(gòu)造方法
5.如果存儲(chǔ)屬性可以提供缺省,那么提倡大家使用設(shè)置缺省值的方式,
這樣可以簡(jiǎn)化代碼(不用自定義構(gòu)造方法,不用寫存儲(chǔ)屬性類型)
classPerson {
varname:String="lzh"
varage:Int
funcdescription() ->String{
return"name =\(name) age =\(age)"
}
init()
{
print("init")
age=30
}
}
1.分配內(nèi)存
2.初始化name和age
3.構(gòu)造方法是隱式調(diào)用的
varp =Person()
p.description()顯示調(diào)用
輸出結(jié)果:init
帶參數(shù)的構(gòu)造方法
classPerson {
varname:String
varage:Int
funcdescription() ->String{
return"name =\(name) age =\(age)"
}
構(gòu)造方法的內(nèi)部參數(shù),默認(rèn)也是外部參數(shù)
而函數(shù)的內(nèi)部參數(shù)默認(rèn)不會(huì)當(dāng)做外部參數(shù)
而方法的內(nèi)部參數(shù),從第二個(gè)開始才會(huì)當(dāng)做外部參數(shù)
init(name:String, age:Int)
構(gòu)造方法對(duì)屬性的順序沒有要求,
只要保證對(duì)象構(gòu)造完時(shí)所有存儲(chǔ)屬性被初始化即可
init(age:Int, name:String)
{
self.name= name
self.age= age
}
funcsetName(name:String, age:Int)
{
}
}
varp =Person(age:30, name:"lzh")
p.setName("lzh", age:30)
funcsetName(name:String, age:Int)
{
}
p.setName("lzh", age:30)
常量存儲(chǔ)屬性與構(gòu)造方法
常量存儲(chǔ)屬性只能通過缺省值或在構(gòu)造方法中被修改
其它任何地方都不能修改
classPerson {
varname:String="lzh"
letage:Int
init(age:Int, name:String)
{
self.age= age
self.name= name
}
funcdescription() ->String{
return"name =\(name) age =\(age)"
}
}
varp =Person(age:30, name:"zs")
print(p3.description())
輸出結(jié)果:name = zs age = 30
常量存儲(chǔ)屬性初始化之后不允許被修改
p.age =10
可選屬性與構(gòu)造方法
classCar {
letname:String
init(name:String)
{
self.name= name
}
}
classPerson {
letname:String
varage:Int
varcar:Car?
可選值存儲(chǔ)屬性可以不再構(gòu)造方法中初始化,
也就是說可選值在對(duì)象構(gòu)造完畢后不用初始化
其實(shí)如果不對(duì)可選存儲(chǔ)屬性進(jìn)行初始化,默認(rèn)就是nil
init(age:Int, name:String)
{
self.age= age
self.name= name
}
funcdescription() ->String{
return"name =\(name) age =\(age) car =\(car)"
}
}
varp =Person(age:10, name:"lzh")
print(p.description())
輸出結(jié)果:name = lzh age = 10 car = nil
結(jié)構(gòu)體構(gòu)造方法
structRect{
此時(shí)即沒有提供缺省值,也沒有提供構(gòu)造方法,但是編譯通過
因?yàn)槟J(rèn)情況下,結(jié)構(gòu)體會(huì)給結(jié)構(gòu)體提供一個(gè)默認(rèn)的成員逐一構(gòu)造器
varwidth:Double=0.0
varheight:Double=0.0
系統(tǒng)默認(rèn)會(huì)提供一個(gè)類似的方法
init(width:Double, height:Double)
{
self.width = width
self.height = height
}
init()
{
self.width = 0.0
self.height = 0.0
}
}
注意:
1.在類中默認(rèn)是沒有逐一構(gòu)造器的
2.如果在結(jié)構(gòu)體中自定義了構(gòu)造方法,那么系統(tǒng)不會(huì)生成默認(rèn)的逐一構(gòu)造器
3.如果給存儲(chǔ)屬性提供了缺省值,系統(tǒng)還是會(huì)提供默認(rèn)的逐一構(gòu)造器
4.如果給存儲(chǔ)屬性提供了缺省值,可以使用不帶參數(shù)的方法初始化結(jié)構(gòu)體
varr =Rect()
print("width=\(r.width),height=\(r.height)")
輸出結(jié)果:width=0.0,height=0.0
varr =Rect(width:10, height:20)
print("width=\(r.width),height=\(r.height)")
輸出結(jié)果:width=10.0,height=20.0
"值類型"的構(gòu)造器代理
構(gòu)造器代理:構(gòu)造方法之間的相互調(diào)用
構(gòu)造方法可以調(diào)用其他構(gòu)造方法來(lái)完成實(shí)例的構(gòu)造,稱之為構(gòu)造器代理
好處:減少構(gòu)造方法之間的重復(fù)代碼
structRect {
varwidth:Double
varheight:Double
init(width:Double, height:Double)
{
self.width= width
self.height= height
}
init()
{
self.width = 0
self.height = 0
構(gòu)造器代理
self.init(width:0, height:0)
}
funcshow(){
print("width =\(width) height =\(height)")
}
}
varr =Rect()
r.show()
輸出結(jié)果:width = 0.0 height = 0.0
varr1 =Rect(width:100, height:100)
r1.show()
輸出結(jié)果:
width = 100.0 height = 100.0
通過閉包或者全局函數(shù)/類方法設(shè)置存儲(chǔ)屬性的缺省值
如果需要經(jīng)過計(jì)算,或者需要進(jìn)行一些額外的操作才能確定初始值時(shí)
就可以通過閉包或全局函數(shù)設(shè)置存儲(chǔ)屬性的缺省值
funcgetValue() ->Int
{
print("getValue")
return55
}
classPerson {
varname:String
系統(tǒng)在初始化的時(shí)候會(huì)隱式執(zhí)行閉包,
將閉包的執(zhí)行結(jié)果賦值給存儲(chǔ)屬性
注意:閉包后面一定要有(),代表執(zhí)行閉包
varage:Int= {
() -> Int in返回值可以省略,
默認(rèn)返回值的類型就是存儲(chǔ)屬性的類型
print("age閉包")
return30
}()
lazyvarheight:Double= {
print("lazy閉包")
return175.0
}()
varage2:Int=getValue()
varage3:Int=Person.getValue2()
不能這樣寫,因?yàn)檎{(diào)用方法時(shí)對(duì)象還沒有初始化完畢
self只有當(dāng)所有的存儲(chǔ)屬性都初始化完畢之后才可以用
var age3:Int = self.getValue3()
init(name:String)
{
print("init")
self.name= name
}
classfuncgetValue2() ->Int{
print("class getValue2")
return100
}
funcgetValue3() ->Int
{
return88
}
}
varp =Person(name:"lzh")
懶加載是用到時(shí)才執(zhí)行,而閉包賦值是初始化時(shí)就會(huì)執(zhí)行
print(p.height)
輸出結(jié)果:
age閉包
getValue
class getValue2
init
lazy閉包
175.0
28- 繼承與構(gòu)造方法, 指定構(gòu)造與便利構(gòu)造方法
指定構(gòu)造與便利構(gòu)造方法
classPerson {
varname:String
varage:Int
指定構(gòu)造方法都是以init開頭的
init(name:String, age:Int)
{
print("init")
self.name= name
self.age= age
}
如果是值類型沒問題,稱之為構(gòu)造器代理
但如果是引用類型會(huì)報(bào)錯(cuò),需要在前面加上convenience關(guān)鍵字.
被convenience關(guān)鍵字修飾的構(gòu)造方法稱之為便利構(gòu)造器,
通過調(diào)用其它構(gòu)造方法來(lái)初始化.
反而言之,便利構(gòu)造器中一定是調(diào)用其它構(gòu)造方法初始化的
一定要出現(xiàn)self.init
convenienceinit()
{
print("convenience init")
self.init(name:”lzh", age:30)
}
類可以擁有多個(gè)構(gòu)造方法
init(name:String)
{
print("init name")
self.name= name
self.age=0
不能在指定構(gòu)造方法中調(diào)用便利構(gòu)造方法
也就是說指定構(gòu)造方法中不能出現(xiàn)self.init
self.init()
}
convenienceinit(age:Int)
{
print("convenience init age")
可以在便利構(gòu)造器中調(diào)用指定構(gòu)造器
self.init(name:”lzh", age:30)
以在便利構(gòu)造器中調(diào)用便利構(gòu)造器
self.init()
}
便利構(gòu)造器不能和指定構(gòu)造器同名
convenience init(name:String)
{
}
}
varp =Person()
輸出結(jié)果:
convenience init
init
varp1 =Person(age:10)
輸出結(jié)果:
convenience init age
convenience init
init
varp2 =Person(name:"lzh")
輸出結(jié)果:
init name
varp3 =Person(name:"lzh", age:20)
輸出結(jié)果:
init
派生類的構(gòu)造方法
classMan {
varname:String
指定構(gòu)造方法
init(name:String){
print("Man init")
self.name= name
}
便利構(gòu)造方法
convenienceinit(){
print("Man convenience init")
self.init(name:"convenience-lzh")
}
}
classSuperMan:Man{
varage:Int
注意:
1.默認(rèn)情況下構(gòu)造方法不會(huì)被繼承
2.基類的存儲(chǔ)屬性只能通過基類的構(gòu)造方法初始化
3.初始化存儲(chǔ)屬性時(shí)必須先初始化當(dāng)前類再初始化父類
4.不能通過便利構(gòu)造方法初始化父類,只能通過調(diào)用指定構(gòu)造方法初始化父類
指定構(gòu)造器
init(age:Int){
self.age= age
super.init(name:"lzh")
不能通過便利構(gòu)造方法初始化父類,只能通過調(diào)用指定構(gòu)造方法初始化父類
以下寫法是錯(cuò)誤的
super.init()
}
funcshow(){
print("age =\(self.age) name=\(self.name)")
}
}
varp =Man()
輸出結(jié)果:
Man convenience init
Man init
varp =SuperMan(age:10)
輸出結(jié)果:
SuperMan init
Man init
構(gòu)造器間的調(diào)用規(guī)則
>指定構(gòu)造器必須調(diào)用其直接父類的"指定構(gòu)造器"
>便利構(gòu)造器必須調(diào)用同類中的其它構(gòu)造器(指定或便利)
>便利構(gòu)造器必須最終以調(diào)用一個(gè)指定構(gòu)造器結(jié)束
(無(wú)論調(diào)用的是指定還是便利,最終肯定會(huì)調(diào)用一個(gè)指定構(gòu)造器)
*指定構(gòu)造器總是向上代理(父類)
*便利構(gòu)造器總是橫向代理(當(dāng)前類)
classMan {
varname:String
指定構(gòu)造方法
init(name:String){
print("Man->init")
self.name= name
}
便利構(gòu)造方法
convenienceinit(){
print("Man->convenience init")
self.init(name:"lzh")
}
}
classSuperMan:Man{
varage:Int
指定構(gòu)造器
init(age:Int){
print("SuperMan->init")
self.age= age
super.init(name:"lzh")
}
convenienceinit(){
print("SuperMan->convenience init")
self.init(age:30)
}
convenienceinit(name:String, age:Int){
print("SuperMan->convenience init name age")
調(diào)用子類構(gòu)造器一定能夠初始化所有屬性
便利構(gòu)造器中只能通過self.init來(lái)初始化,不能使用super.init
因?yàn)檎{(diào)用父類構(gòu)造器不一定能完全初始化所有屬性(子類特有)
super.init(name: “l(fā)zh")
self.init()
}
funcshow(){
print("name =\(self.name) age =\(self.age)")
}
}
varp =SuperMan()
p.show()
輸出結(jié)果:
SuperMan->convenience init
SuperMan->init
Man->init
name = lzh age = 30
varp =SuperMan(age:10)
p.show()
輸出結(jié)果:
SuperMan->init
Man->init
name = lzh age = 10
varp =SuperMan(name:"lzh", age:20)
p.show()
輸出結(jié)果:
SuperMan->convenience init name age
SuperMan->convenience init
SuperMan->init
Man->init
name = lzh age = 30
兩段式構(gòu)造
構(gòu)造過程可以劃分為兩個(gè)階段
1.確保當(dāng)前類和父類所有存儲(chǔ)屬性都被初始化
2.做一些其它初始化操作
好處: 1.可以防止屬性在被初始化之前訪問
2.可以防止屬性被另外一個(gè)構(gòu)造器意外賦值
注意:構(gòu)造器的初始化永遠(yuǎn)是在所有類的第一階段初始化完畢之后才會(huì)開始第二階段
編譯器安全檢查:
1.必須先初始化子類特有屬性,再向上代理父類指定構(gòu)造方法初始化父類屬性
2.只能在調(diào)用完父類指定構(gòu)造器后才能訪問父類屬性
3.在便利構(gòu)造器中,必須先調(diào)用同類其它構(gòu)造方法后才能訪問屬性
4.第一階段完成前不能訪問父類屬性/也不能引用self和調(diào)用任何實(shí)例方法
classMan {
varname:String
指定構(gòu)造方法
init(name:String){
self.name= name
}
便利構(gòu)造方法
convenienceinit(){
self.init(name:"lnj")
}
}
classSuperMan:Man{
varage:Int
指定構(gòu)造器
init(age:Int){
print("SuperMan第一階段開始")
對(duì)子類引入的屬性初始化
self.age= age
代碼會(huì)報(bào)錯(cuò),因?yàn)檎{(diào)用self.name之前還沒有對(duì)父類的name進(jìn)行初始化
即便在這個(gè)地方修改,也會(huì)被后面的初始化語(yǔ)句覆蓋
if (age > 30){
self.name = "zs"
}
對(duì)父類引入的屬性進(jìn)行初始化
super.init(name:"lzh")
print("SuperMan第二階段開始")
ifage >30{
self.name="zs"
}
}
}
classMonkeyMan:SuperMan{
varheight:Double
init(height:Double){
print("MonkeyMan第一階段開始")
對(duì)子類引入的屬性初始化
self.height=99.0
對(duì)父類引入的屬性進(jìn)行初始化
super.init(age:30)
print("MonkeyMan第二階段開始")
ifheight <100.0{
self.age=50
}
}
}
varm =MonkeyMan(height:20)
輸出結(jié)果:
MonkeyMan第一階段開始
SuperMan第一階段開始
SuperMan第二階段開始
MonkeyMan第二階段開始
重寫指定構(gòu)造方法
子類的構(gòu)造方法和父類的一模一樣
classMan {
varname:String
指定構(gòu)造方法
init(name:String){
self.name= name
}
}
classSuperMan:Man{
varage:Int
init(){
self.age=30
super.init(name:“l(fā)zh")
}
如果子類中的構(gòu)造器和父類一模一樣
必須加上override關(guān)鍵字,表示重寫父類方法
在老版本的Swift語(yǔ)言中是不需要override關(guān)鍵字的,新版本才推出的
override init(name:String){
self.age = 30
super.init(name: name)
}
將父類的指定構(gòu)造器重寫成一個(gè)便利構(gòu)造器
也必須加上override關(guān)鍵字,表示重寫父類方法
convenienceoverrideinit(name:String){
self.init(name:name)
self.age=30
}
}
varp =SuperMan()
輸出結(jié)果:
SuperMan init
Man init
便利構(gòu)造方法不存在重寫
classMan {
varname:String
指定構(gòu)造方法
init(name:String){
print("Man->init")
self.name= name
}
convenienceinit(){
print("Man->convenience init")
self.init(name:"lzh")
}
}
classSuperMan:Man{
varage:Int
init(age:Int){
print("SuperMan->init")
self.age= age
super.init(name:"lzh")
}
Swift中便利構(gòu)造方法不存在重寫,如果加上override關(guān)鍵字
系統(tǒng)會(huì)去查找父類中有沒有和便利構(gòu)造方法一樣的指定構(gòu)造方法
有就不報(bào)錯(cuò),沒有就報(bào)錯(cuò)
為什么便利構(gòu)造器不能重寫呢?
因?yàn)楸憷麡?gòu)造器只能橫向代理
只能調(diào)用當(dāng)前類的其它構(gòu)造方法或指定方法,不可能調(diào)用super.所以不存在重寫
也就是說子類的便利構(gòu)造方法不能直接訪問父類的便利構(gòu)造方法,所以不存在重寫的概念
convenienceinit(){
print("SuperMan-> convenience init")
self.init(age:30)
}
}
varsm =SuperMan()
輸出結(jié)果:
SuperMan-> convenience init
SuperMan->init
Man->init
構(gòu)造方法的自動(dòng)繼承
1.如果子類中沒有定義任何構(gòu)造器,且子類中所有的存儲(chǔ)屬性都有缺省值,
會(huì)繼承父類中所有的構(gòu)造方法(包括便利構(gòu)造器)
2.如果子類中只是重寫了父類中的某些指定構(gòu)造器
不管子類中的存儲(chǔ)屬性是否有缺省值,都不會(huì)繼承父類中的其它構(gòu)造方法
3.如果子類重寫了父類中所有的指定構(gòu)造器
不管子類中的存儲(chǔ)屬性是否有缺省值,都會(huì)同時(shí)繼承父類中的所有便利方法
classPerson {
varname:String
varage:Int
init(name:String, age:Int){
print("Person init name age")
self.name= name
self.age= age
}
init(name:String){
print("Person init name")
self.name= name
self.age=0
}
convenienceinit(){
print("Person convenience init")
self.init(name:"lzh")
}
}
classSuperMan:Person{
varheight:Double=175.0
}
如果子類中沒有定義任何構(gòu)造器,
且子類中所有的存儲(chǔ)屬性都有缺省值,會(huì)繼承父類中所有的構(gòu)造方法(包括便利構(gòu)造器).
父類的存儲(chǔ)屬性是由父類的構(gòu)造器初始化,子類的存儲(chǔ)屬性是由缺省值初始化的.
varp =SuperMan()
如果子類中只是重寫了父類中的某些指定構(gòu)造器
不管子類中的存儲(chǔ)屬性是否有缺省值,都不會(huì)繼承父類中的其它構(gòu)造方法
classSuperMan:Person{
varheight:Double=175.0
init(height:Double){
self.height= height
super.init(name:“l(fā)zh", age:30)
}
}
varp =SuperMan()
如果子類重寫了父類中所有的指定構(gòu)造器
不管子類中的存儲(chǔ)屬性是否有缺省值,都會(huì)同時(shí)繼承父類中的所有便利方法
classSuperMan:Person{
varheight:Double
init(height:Double){
self.height= height
super.init(name:"lzh", age:30)
}
overrideinit(name:String, age:Int){
self.height=175.0
super.init(name: name, age: age)
}
overrideinit(name:String){
self.height=175.0
super.init(name: name)
}
}
必須構(gòu)造器
只要在構(gòu)造方法的前面加上一個(gè)required關(guān)鍵字
那么所有的子類(后續(xù)子類)只要定義了構(gòu)造方法都必須實(shí)現(xiàn)該構(gòu)造方法
classPerson {
varname:String
早期Swift版本中沒有這個(gè)語(yǔ)法
requiredinit(name:String){
print("Person init")
self.name= name
}
}
classSuperMan:Person{
varage:Int=30
如果子類沒有定義構(gòu)造器,可以不用重寫
init(){
print("SuperMan init")
self.age=30
super.init(name:"lzh")
}
1.如果子類自定義了構(gòu)造器,就必須重寫"必須構(gòu)造器"
因?yàn)槿绻宇悰]有自定義任何構(gòu)造器,默認(rèn)會(huì)繼承父類構(gòu)造器,所以不用重寫
2.重寫必須構(gòu)造器不用加override關(guān)鍵字
因?yàn)橄到y(tǒng)看到required關(guān)鍵字就會(huì)自動(dòng)查看父類
為什么重寫了還需要加上required關(guān)鍵字,因?yàn)樗泻罄m(xù)子類都必須重寫
requiredinit(name:String) {
print("SuperMan init name")
self.age=30
super.init(name:name)
}
}
varsm =SuperMan(name:"lzh")
輸出結(jié)果:
SuperMan init name
Person init
29- 析構(gòu)方法
析構(gòu)方法
對(duì)象的內(nèi)存被回收前夕被隱式調(diào)用的方法,對(duì)應(yīng)OC的dealloc方法
主要執(zhí)行一些額外操作,例如釋放一些持有資源,關(guān)閉文件,斷開網(wǎng)絡(luò)等
classFileHandler{
varfd:Int32?文件描述符
指定構(gòu)造器
init(path:String){
需要打開的文件路徑,打開方式(只讀)
open方法是UNIX的方法
letret =open(path,O_RDONLY)
ifret == -1{
fd=nil
}else{
fd= ret
}
print("對(duì)象被創(chuàng)建")
}
析構(gòu)方法
deinit{
關(guān)閉文件
ifletofd =fd{
close(ofd)
}
print("對(duì)象被銷毀")
}
}
varfh:FileHandler? =
FileHandler(path:"/Users/gaoxinqiang/Desktop/lzh.jpg")
當(dāng)對(duì)象沒有任何強(qiáng)引用時(shí)會(huì)被銷毀
fh=nil
輸出結(jié)果:
對(duì)象被創(chuàng)建
對(duì)象被銷毀
析構(gòu)方法的自動(dòng)繼承
父類的析構(gòu)方法會(huì)被自動(dòng)調(diào)用,不需要子類管理
classPerson {
varname:String
init(name:String){
self.name= name
print("Person init")
}
deinit{
print("Person deinit")
}
}
classSuperMan:Person{
varage:Int
init(age:Int){
self.age= age
super.init(name:"xmg")
print("SuperMan init")
}
deinit{
如果父類的析構(gòu)方法不會(huì)被自動(dòng)調(diào)用,那么我們還需要關(guān)心父類
但是如果這樣做對(duì)子類是比較痛苦的
print("SuperMan deinit")
}
}
varsm:SuperMan? =SuperMan(age:30)
sm=nil
輸出結(jié)果:
Person init
SuperMan init
SuperMan deinit
Person deinit
30- Swift內(nèi)存管理
Swift內(nèi)存管理:
管理引用類型的內(nèi)存,不會(huì)管理值類型,值類型不需要管理
內(nèi)存管理原則:當(dāng)沒有任何強(qiáng)引用指向?qū)ο?系統(tǒng)會(huì)自動(dòng)銷毀對(duì)象
(默認(rèn)情況下所有的引用都是強(qiáng)引用)
如果做到該原則: ARC
classPerson {
varname:String
init(name:String){
self.name= name
}
deinit{
print("deinit")
}
}
varp:Person? =Person(name:"xmg")
p=nil
輸出結(jié)果:deinit
weak弱引用
classPerson {
varname:String
init(name:String){
self.name= name
}
deinit{
print("deinit")
}
}
強(qiáng)引用,引用計(jì)數(shù)+1
varstrongP =Person(name:"xmg")1
varstrongP2 =strongP2
弱引用,引用計(jì)數(shù)不變
如果利用weak修飾變量,當(dāng)對(duì)象釋放后會(huì)自動(dòng)將變量設(shè)置為nil
所以利用weak修飾的變量必定是一個(gè)可選類型,因?yàn)橹挥锌蛇x類型才能設(shè)置為nil
weakvarweakP:Person? =Person(name:"xmg")
ifletp =weakP{
print(p)
}else
{
print(weakP)
}
輸出結(jié)果:
deinit
nil
unowned無(wú)主引用,相當(dāng)于OC unsafe_unretained
unowned和weak的區(qū)別:
1.利用unowned修飾的變量,對(duì)象釋放后不會(huì)設(shè)置為nil.不安全
利用weak修飾的變量,對(duì)象釋放后會(huì)設(shè)置為nil
2.利用unowned修飾的變量,不是可選類型
利用weak修飾的變量,是可選類型
classPerson {
varname:String
init(name:String){
self.name= name
}
deinit{
print("deinit")
}
}
unownedvarweakP:Person=Person(name:"xmg")
循環(huán)引用
ARC不是萬(wàn)能的,它可以很好的解決內(nèi)存問題,但是在某些場(chǎng)合不能很好的解決內(nèi)存泄露問題
例如兩個(gè)或多個(gè)對(duì)象之間的循環(huán)引用問題
classPerson {
letname:String姓名
人不一定有公寓
如果這里不加weak的話,當(dāng)對(duì)象設(shè)置為nil時(shí),該對(duì)象并沒有被銷毀.
weakvarapartment:Apartment?公寓
init(name:String){
self.name= name
}
deinit{
print("\(self.name) deinit")
}
}
classApartment {
letnumber:Int房間號(hào)
vartenant:Person?租客
init(number:Int){
self.number= number
}
deinit{
print("\(self.number) deinit")
}
}
varp:Person? =Person(name:"xmg")
vara:Apartment? =Apartment(number:888)
p!.apartment=a人有一套公寓
a!.tenant=p!公寓中住著一個(gè)人
p=nil
a=nil
輸出結(jié)果:
888 deinit
xmg deinit
classPerson {
letname:String姓名
人不一定有信用卡
varcard:CreditCard?
init(name:String){
self.name= name
}
deinit{
print("\(self.name) deinit")
}
}
classCreditCard{
letnumber:Int
信用卡必須有所屬用戶
當(dāng)某一個(gè)變量/常量必須有值,一直有值,那么可以使用unowned修飾
unownedletperson:Person
init(number:Int, person:Person){
self.number= number
self.person= person
}
deinit{
print("\(self.number) deinit")
}
}
varp:Person? =Person(name:"xmg")
varcc:CreditCard? =CreditCard(number:8888888, person:p!)
p=nil
cc=nil
輸出結(jié)果:
xmg deinit
8888888 deinit
31- swift可選類型
可選類型:
可選類型的本質(zhì)其實(shí)就是一個(gè)枚舉
None沒有值
Some有值
格式: Optional<類型>或在類型后面加上?號(hào)
由于可選類型在Swift中隨處可見,所以系統(tǒng)做了一個(gè)語(yǔ)法糖,在類型后面加上?
varopa:Optional
varopb:Int?
varnora:Int
nora=10
print(nora)
print(opb)
輸出結(jié)果:
10
nil
基本類型變量,在使用之前必須進(jìn)行初始化,否則報(bào)錯(cuò)
目的:安全,不管在什么時(shí)候訪問都是有意義的
普通變量和可選類型的區(qū)別,普通變量只有一種狀態(tài),有值
注意:Swift的變量和C/OC的不一樣, C/OC可以沒有值,是一個(gè)隨機(jī)值
可選類型是安全的么?是,可以通過可選綁定判斷后再使用
Swift的發(fā)明者完全是基于安全的考慮,當(dāng)我們使用基本類型時(shí)完全不用考慮是否有值
當(dāng)我們使用可選類型時(shí),總會(huì)記得先判斷再使用.讓程序時(shí)刻了解哪些有值哪有沒有值
varopb:Int?
opb=55
ifletb =opb{
print(opb!)
print(b)
}
輸出結(jié)果:
55
55
可選鏈
通過可選類型的變量來(lái)調(diào)用相應(yīng)的屬性和方法
可選鏈的返回值是一個(gè)可選值
格式:
可選值?.屬性
可選值?.方法
classPerson {
varname:String
init(name:String){
self.name= name
}
funcwhoami() ->String{
print("my name is\(self.name)")
returnname
}
}
varp0 :Person?
varp1 :Person=Person(name:"xmg")
p1.name="zs"
p1.whoami()
輸出結(jié)果:
my name is zs
如何通過可選類型來(lái)調(diào)用對(duì)應(yīng)的方法和屬性
1.通過強(qiáng)制解包
但是強(qiáng)制解包非常危險(xiǎn),如果可選類型沒有值,會(huì)引發(fā)運(yùn)行時(shí)錯(cuò)誤
p0!.name="ls"
p0!.whoami()
2.通過可選綁定,代碼繁瑣
ifletp =p0{
p.name="ls"
p.whoami()
}
3.通過可選鏈,如果問號(hào)前面的變量沒有值,整個(gè)可選鏈會(huì)失效
p0=p1
p0?.name="ls"
p0?.whoami()
可選鏈的返回值會(huì)自動(dòng)包裝成一個(gè)可選值
因?yàn)榭蛇x鏈可用能失效
所以返回值可能有值也可能沒有值
要想表達(dá)有值或者沒有值只能用可選值,所以返回值會(huì)自動(dòng)包裝成一個(gè)可選值
print(p0?.name)
print(p0?.whoami())
print(p1.name)
vara:String? =p0?.name
p0?.name="ww"
varb:String=p1.name
輸出結(jié)果:
nil
nil
xmg
可選鏈調(diào)用下標(biāo)索引
格式:
可選值?[]
structStudent {
varname:String="xmg"
varmath:Double=99.0
varchinese:Double=99.0
varenglish:Double=99.0
要想實(shí)現(xiàn)下標(biāo)訪問,必須實(shí)現(xiàn)subscript方法
如果想要通過下標(biāo)訪問,必須實(shí)現(xiàn)get方法
如果想要通過下表賦值,必須實(shí)現(xiàn)set方法
subscript(course:String) ->Double?{
get{
switchcourse{
case"math":
returnmath
case"chinese":
returnchinese
case"english":
returnenglish
default:
returnnil
}
}
set{
switchcourse{
case"math":
因?yàn)榉祷氐氖强蛇x類型
math= newValue!
case"chinese":
chinese= newValue!
case"english":
english= newValue!
default:
print("not found")
}
}
}
}
varstu:Student? =Student()
可選鏈調(diào)用下標(biāo)索引不需要.,直接在問號(hào)后面寫上[]即可
print(stu?["math"])
輸出結(jié)果:Optional(99.0)
利用可選鏈賦值.注意:早起版本中不能利用可選鏈賦值
stu?.name="ww"
print(stu?.name)
輸出結(jié)果:Optional("ww")
利用可選鏈給下標(biāo)賦值
stu?["math"]=88
print(stu?["math"])
輸出結(jié)果:Optional(88.0)
判斷賦值操作是否成功,可選鏈的賦值操作也有返回值
如果賦值成功會(huì)返回一個(gè)可選類型
返回()?也就是Viod?代表成功.
返回nil代表失敗
letres:Void? =stu?.name="zl"
print(res)
輸出結(jié)果:Optional(())代表成功.
stu=nil
letres:Void? =stu?.name="zl"
print(res)
輸出結(jié)果:nil代表失敗
多層可選鏈:
單層:可選值?.屬性
多層:可選值?.屬性.屬性?.屬性或者可選值?.屬性?.屬性?.屬性
classA {
varname:String="xmg"
}
classB{
vara1:A?
}
classC{
varb1:B=B()
}
classD{
varc1:C?
}
vara1 =A()
varb1 =B()
varc1 =C()
vard1 =D()
d1.c1=c1
通過d直接給b賦值
由于D中的C是可選值,所以需要在C后面加上?
d1.c1?.b1.a1=a1
通過d直接獲取a中的name
其實(shí)只需要在可選值后面加上問號(hào)即可,如果可選值不存在,那么后面的鏈?zhǔn)?/p>
print(d1.c1?.b1.a1?.name)
輸出結(jié)果:Optional("xmg")