10 結(jié)構(gòu)體和類 - —— 《Swift3.0 從入門到出家》
結(jié)構(gòu)體和類
Swift中的面向?qū)ο?個(gè)要素:枚舉矮嫉、結(jié)構(gòu)體滑臊、類痰娱、協(xié)議弃榨、擴(kuò)展
面向?qū)ο笱芯康氖菍?duì)象,完成一件事情需要多個(gè)對(duì)象參與梨睁,是生活的映射
Swift中結(jié)構(gòu)體和類非常相似鲸睛,也就是結(jié)構(gòu)體能完成類的所有功能。結(jié)構(gòu)體是值類型坡贺,類是引用類型
結(jié)構(gòu)體
結(jié)構(gòu)體定義格式:
struct 結(jié)構(gòu)體名稱{
成員變量(數(shù)據(jù)類型定義的變量)—屬性
成員方法(函數(shù)) —行為
}
結(jié)構(gòu)體的成員變量不要求初始化
如果結(jié)構(gòu)體的成員變量賦初值官辈,編譯器會(huì)自動(dòng)生成一個(gè)不帶參數(shù)的構(gòu)造器,原有的之一構(gòu)造器還可以繼續(xù)使用
結(jié)構(gòu)體自動(dòng)提供了逐一構(gòu)造器init遍坟,對(duì)對(duì)象或者實(shí)例的所有屬性賦值
結(jié)構(gòu)體中的成員方法不能直接修改成員變量的值拳亿,如果修改成員變量的值,需要使用關(guān)鍵字mutating修飾該成員方法
重點(diǎn):
通過實(shí)例或者對(duì)象調(diào)用的方法稱為實(shí)例方法
類方法只能用類型名稱(結(jié)構(gòu)體類型名/類名)調(diào)用
static或者class修飾的函數(shù)愿伴,稱其為類方法风瘦,class修飾函數(shù)只能類中使用
結(jié)構(gòu)體實(shí)例方法可以直接訪問結(jié)構(gòu)體的成員變量
結(jié)構(gòu)體的類方法默認(rèn)不能訪問結(jié)構(gòu)體中的成員變量
實(shí)例方法可以直接調(diào)用其他實(shí)例方法,調(diào)用類方法可以直接使用類名調(diào)用
類方法中可以直接調(diào)用其他類方法公般,不能直接調(diào)用實(shí)例方法
init構(gòu)造器
如果自定義的構(gòu)造方法和系統(tǒng)提供的構(gòu)造方法同名万搔,系統(tǒng)的構(gòu)造方法被替代
構(gòu)造器中用self關(guān)鍵字對(duì)成員變量進(jìn)行初始化胡桨,誰(shuí)調(diào)用了該方法,self就相當(dāng)于誰(shuí)
類
類的定義格式:
class 類名{
成員變量
成員方法
}
結(jié)構(gòu)體和類的區(qū)別
- Swift中的類沒有統(tǒng)一的父類
- 類中的成員變量必須初始化瞬雹,可以直接在定義變量的時(shí)候賦值昧谊,也可以自定義構(gòu)造 方法賦值,如果自定義了構(gòu)造方法酗捌,無(wú)論和系統(tǒng)提供的構(gòu)造方法是否相同呢诬,系統(tǒng)提供的構(gòu)造方法就不能再使用了
- 類中實(shí)例方法可以直接修改類中的成員變量
- 類提供的構(gòu)造方法是沒有參數(shù)的
- 類是引用類型,而結(jié)構(gòu)體是值類型
- 類可以被繼承胖缤,結(jié)構(gòu)體不能被繼承
- 與值類型不同尚镰,引用類型在被賦予到一個(gè)變量、常量或者被傳遞到一個(gè)函數(shù)時(shí)哪廓,操作的并不是其拷貝
- 一個(gè)類可以被定義成多個(gè)常量狗唉,定義了常量后,其數(shù)值不會(huì)再發(fā)生改變涡真,沒一次創(chuàng)建一個(gè)常量分俯,修改的是后臺(tái)類的值
可失敗構(gòu)造器 --- 可以類中定義可以在結(jié)構(gòu)體中定義
當(dāng)使用構(gòu)造器創(chuàng)建對(duì)象時(shí) 可以向構(gòu)造器傳遞的形參無(wú)效 或者在構(gòu)造器中使用函數(shù)外部的資源缺失 就會(huì)造成創(chuàng)建對(duì)象失敗 如果創(chuàng)建對(duì)象失敗 調(diào)用任何實(shí)例方法都會(huì)崩潰 為了解決崩潰問題 使用可失敗構(gòu)造器 將崩潰的結(jié)果變成nil
init? 創(chuàng)建的對(duì)象可能存在nil值 所以當(dāng)對(duì)象創(chuàng)建成功 需要強(qiáng)制解析
init! 創(chuàng)建的對(duì)象相當(dāng)于使用了隱式解析 使用隱式解析或者強(qiáng)制解析的前提條件 確保對(duì)象真實(shí)存在
可失敗構(gòu)造器 必須在滿足某個(gè)條件的情況下 才調(diào)用return nil的語(yǔ)句 證明對(duì)象創(chuàng)建失敗
【注意】定義的可失敗的構(gòu)造器 一定不能和非可失敗的構(gòu)造器參數(shù)名稱相同 類型相同 參數(shù)個(gè)數(shù)相同
例子:
init?(name: String) {//可失敗構(gòu)造器,可能有這個(gè)屬性哆料,也可能沒這個(gè)屬性
if name.isEmpty {
return nil
}
self.name = name
self.age = 10
self.height = 1.0
}
func study() -> Void {
print("正在寫代碼")
}
}
調(diào)用
var stu1 = Student.init(姓名: "韓梅梅", 年齡: 10, 身高: 1.20)
stu1.study()
var stu2 = Student.init()
stu2.study()
var stu3 = Student.init(name: "")
print(stu3)
stu3?.study()
類和結(jié)構(gòu)體的選擇
在你的代碼中缸剪,可以使用類和結(jié)構(gòu)體來(lái)的自定義數(shù)據(jù)類型
結(jié)構(gòu)體總是通過值傳遞 類實(shí)例總是通過引用傳遞,意味著兩者適用不同的任務(wù)
按照通用的準(zhǔn)則东亦,當(dāng)符合一條或者多條以下條件時(shí)杏节,請(qǐng)考慮使用 構(gòu)建結(jié)構(gòu)體
<1>結(jié)構(gòu)體的主要目的是用來(lái)盛裝少量相關(guān)簡(jiǎn)單數(shù)據(jù)值
<2>有理由預(yù)計(jì)一個(gè)結(jié)構(gòu)體實(shí)例在賦值或者傳遞時(shí),封裝的數(shù)據(jù)將會(huì)被拷貝而不是被引用
<3>任何在結(jié)構(gòu)體中儲(chǔ)存的值類型屬性典阵,也將會(huì)被拷貝拢锹,而不是被引用
<4>結(jié)構(gòu)體不需要去繼承另一個(gè)已存在類型的屬性或行為
合適的結(jié)構(gòu)體候選者包括:
<1>幾何形狀的大小,封裝一個(gè)width屬性和height屬性萄喳,兩者均為Double類型
<2>一定范圍內(nèi)的路徑卒稳,封裝一個(gè)start屬性和length屬性,兩者均為Int類型
三維坐標(biāo)系內(nèi)一點(diǎn)他巨,封裝x充坑,y和z的屬性,三者均為Double類型
屬性觀察者
屬性觀察者是用來(lái)觀察屬性變化前和變化后的值
willSet【(newValue)】
willSet方法在被觀察的屬性將要發(fā)生變化時(shí)調(diào)用該方法 該方法默認(rèn)攜帶一個(gè)參數(shù)newValue 表示屬性的新值
didSet【(oldValue)】
** didSet**方法在被觀察的屬性已經(jīng)變化之后調(diào)用該方法 這個(gè)方法也攜帶一個(gè)默認(rèn)參數(shù)oldValue 表示屬性的舊值
和OC語(yǔ)言中KVO原理相同
例子:
class Person {
//一個(gè)屬性帶有一個(gè)觀察者
var age = 1 {
willSet {
//觀察屬性將要變化的值
print("預(yù)祝你\(newValue)歲生日快樂??")
}
didSet {
//觀察屬性變化以后的值
print("恭喜你從\(oldValue)歲到\(age)歲")
}
}
//改變age的值
func happyBirthday() -> Void {
age += 1
}
}
**調(diào)用: **
var xiaoPerson = Person.init()
for i in 1...6 {
xiaoPerson.happyBirthday()
}
<練習(xí)>游戲公司觀察1到6月份游戲的下載量 如果當(dāng)月游戲下載量超過10萬(wàn) 工資翻倍
struct gameDownLoadNum {
var downLoadNum = 0 {
willSet {
print("本月的游戲下載量將要到達(dá)\(newValue)萬(wàn)次")
}
didSet {
print("上個(gè)月的下載量為\(oldValue)萬(wàn)次 本月的下載量為\(downLoadNum)萬(wàn)次,請(qǐng)注意查看")
if downLoadNum >= 10 {
print("恭喜你當(dāng)月工資翻倍")
}
else {
print("下載量未超過10萬(wàn)繼續(xù)努力")
}
}
}
mutating func changeNum() -> Void {
downLoadNum = Int(arc4random()) % 20
}
}
調(diào)用
var num = gameDownLoadNum.init()
for i in 1...6 {
num.changeNum()
}