對(duì) Swift 學(xué)習(xí) 的一些總結(jié)
學(xué)習(xí)文獻(xiàn):
Chris Eidhof. “Swift 進(jìn)階”
類與結(jié)構(gòu)體的主要不同點(diǎn)
- 語(yǔ)義:
類:
引用類型(引用語(yǔ)義)弄匕,需要自己管理其引用計(jì)數(shù)、引用值得變化
結(jié)構(gòu)體:值類型(值語(yǔ)義)嘀粱,在設(shè)計(jì)結(jié)構(gòu)體時(shí),我們可以要求編譯器保證不可變性侨核。
- 內(nèi)存管理方式:
類:
類的實(shí)例只能通過(guò)引用來(lái)間接地訪問(wèn)草穆。類能有很多個(gè)持有者。
結(jié)構(gòu)體:可以被直接持有及訪問(wèn)搓译,不會(huì)被引用悲柱,但是會(huì)被復(fù)制。也就是說(shuō)些己,結(jié)構(gòu)體的持有者是唯一的豌鸡。
- 共享代碼:
類:
通過(guò)繼承來(lái)共享代碼
結(jié)構(gòu)體 (以及枚舉):不能被繼承嘿般。想要在不同的結(jié)構(gòu)體或者枚舉之間共享代碼,我們需要使用不同的技術(shù)涯冠,比如像是組合炉奴、泛型以及協(xié)議擴(kuò)展等。
值類型
值永遠(yuǎn)不會(huì)改變蛇更,它們具有不可變的特性瞻赶。這 (在絕大多數(shù)情況下) 是一件好事,因?yàn)槭褂貌蛔兊臄?shù)據(jù)可以讓代碼更容易被理解派任。不可變性也讓代碼天然地具有線程安全的特性砸逊,因?yàn)椴荒芨淖兊臇|西是可以在線程之間安全地共享的。
值語(yǔ)義
- 結(jié)構(gòu)體不能通過(guò)引用來(lái)進(jìn)行比較掌逛,只能通過(guò)它們的屬性來(lái)比較兩個(gè)結(jié)構(gòu)體师逸。雖然可以用 var 來(lái)在結(jié)構(gòu)體中聲明可變的變量屬性,但是這個(gè)可變性只體現(xiàn)在變量本身上豆混,而不是指里面的值篓像。改變一個(gè)結(jié)構(gòu)體變量的屬性,在概念上來(lái)說(shuō)皿伺,和為整個(gè)變量賦值一個(gè)全新的結(jié)構(gòu)體是等價(jià)的员辩。我們總是使用一個(gè)新的結(jié)構(gòu)體,并設(shè)置被改變的屬性值心傀,然后用它替代原來(lái)的結(jié)構(gòu)體屈暗。
- 結(jié)構(gòu)體只有一個(gè)持有者。比如脂男,當(dāng)我們將結(jié)構(gòu)體變量傳遞給一個(gè)函數(shù)時(shí)养叛,函數(shù)將接收到結(jié)構(gòu)體的復(fù)制,它也只能改變它自己的這份復(fù)制宰翅。這叫做值語(yǔ)義 (value semantics) (又作復(fù)制語(yǔ)義)
引用語(yǔ)義
類對(duì)象會(huì)擁有很多持有者弃甥,這被叫做引用語(yǔ)義 (reference semantics)。
結(jié)構(gòu)體
結(jié)構(gòu)體為值類型
值類型意味著一個(gè)值變量被復(fù)制時(shí)汁讼,這個(gè)值本身也會(huì)被復(fù)制淆攻,而不只限于對(duì)這個(gè)值的引用的復(fù)制。在幾乎所有的編程語(yǔ)言中嘿架,標(biāo)量類型都是值類型瓶珊。這意味著當(dāng)一個(gè)值被賦給新的變量時(shí),并不是傳遞引用耸彪,而是進(jìn)行值的復(fù)制伞芹。
let a = 12
let b = a
a += 1
print (a) // 13
print (b) // 12
b的值沒(méi)有根據(jù)a的值進(jìn)行改變
結(jié)構(gòu)體的賦值 特點(diǎn)
- 定義結(jié)構(gòu)體 PY_Point 它里面有一個(gè)
let x = 12
, 以及var y
, 可以比較 y是否大于x
struct PY_Point {
let x = 12
var y = 0
func isReaterThanX() -> Bool {
return y > x
}
}
- 賦值: 下面打印結(jié)果表示 兩個(gè)結(jié)構(gòu)體已經(jīng)不是同一個(gè)結(jié)構(gòu)體了。發(fā)生了復(fù)制現(xiàn)象唱较。
var point = PY_Point(y: 13)
var pointTemp = point
withUnsafePointer(to: &point) { pointAddress in
print("point 地址: \(pointAddress)")
}
withUnsafePointer(to: &pointTemp) {pointTempAddress in
print("pointTemp 地址: \(pointTempAddress)")
}
/**
point 地址: 0x00007ffeec4d7a00
pointTemp 地址: 0x00007ffeec4d7a10
*/
- 結(jié)構(gòu)體(值類型)內(nèi)部賦值:
可以看到扎唾,多次調(diào)用了didSet 方法,但是pointTemp的地址沒(méi)有改變
“理解值類型的關(guān)鍵就是理解為什么這里會(huì)被調(diào)用南缓。對(duì)結(jié)構(gòu)體進(jìn)行改變胸遇,在語(yǔ)義上來(lái)說(shuō),與重新為它進(jìn)行賦值是相同的汉形。當(dāng)我們改變了結(jié)構(gòu)體中某個(gè)深層次的屬性時(shí)纸镊,其實(shí)還是意味著我們改變了結(jié)構(gòu)體,所以 didSet 依然會(huì)被觸發(fā)获雕。
雖然語(yǔ)義上來(lái)說(shuō)薄腻,我們將整個(gè)結(jié)構(gòu)體替換為了新的結(jié)構(gòu)體,但是編譯器依然會(huì)原地進(jìn)行變更届案。由于這個(gè)結(jié)構(gòu)體沒(méi)有其他所有者,實(shí)際上我們沒(méi)有必要進(jìn)行復(fù)制罢艾。不過(guò)如果有多個(gè)持有者的話楣颠,重新賦值意味著發(fā)生復(fù)制。對(duì)于寫時(shí)復(fù)制的結(jié)構(gòu)體咐蚯,工作方式會(huì)略有不同
var pointTemp: PY_Point? = PY_Point(y: 13) {
didSet {
print("pointTemp didSet 調(diào)用了")
}
}
func valuation() {
withUnsafePointer(to: &pointTemp) {pointTempAddress in
print("pointTemp 地址: \(pointTempAddress)")
}
pointTemp?.y = -1
withUnsafePointer(to: &pointTemp) {pointTempAddress in
print("pointTemp 地址: \(pointTempAddress)")
}
}
//調(diào)用valuation()打印結(jié)果為
/**
pointTemp 地址: 0x00007ffee9379a00
pointTemp didSet 調(diào)用了
pointTemp didSet 調(diào)用了
pointTemp 地址: 0x00007ffee9379a00
pointTemp didSet 調(diào)用了
*/
結(jié)構(gòu)體內(nèi)建方法
- 結(jié)構(gòu)體可以添加方法 ,比如比較x童漩,y大小的方法
struct PY_Point {
let x = 12
var y = 0
func isReaterThanX() -> Bool {
return y > x
}
}
- 結(jié)構(gòu)體方法中修改自身的屬性值
編譯器自動(dòng)補(bǔ)充了關(guān)鍵詞 mutating
對(duì)不可變x屬性,提示了Left side of mutating operator isn't mutable: 'x' is a 'let' constant (x為let 修飾春锋,值不能被修改)
extension PY_Point {
mutating func y_offset1() {
y += 1
}
mutating func x_offset1() {
///報(bào)錯(cuò) Left side of mutating operator isn't mutable: 'x' is a 'let' constant
x += 1
}
}
mutating / inout關(guān)鍵詞
- inout: 函數(shù)中矫膨,我們可以將參數(shù)標(biāo)記為 inout 來(lái)使其可變。就和一個(gè)普通的參數(shù)一樣期奔,值被復(fù)制并作為參數(shù)被傳到函數(shù)內(nèi)侧馅。不過(guò),我們可以改變這個(gè)復(fù)制 (就好像它是被 var 定義的一樣)呐萌。然后當(dāng)函數(shù)返回時(shí)馁痴,原來(lái)的值將被覆蓋掉。
postfix func ++ (left: inout CGFloat) {
left += 1
}
var a:CGFloat = 1
a++
print(a)
// 打印結(jié)果為 2.0
-
mutating: 函數(shù)體內(nèi)部你可以隨時(shí)使用 self肺孤。如果我們想要改變 self罗晕,或是改變 self 自身或者嵌套的 (比如 self.y) 任何屬性,我們就需要將方法標(biāo)記為 mutating.
mutating 將隱式的 self 參數(shù)變?yōu)榭勺兊脑拢⑶腋淖兞诉@個(gè)變量的值小渊。
- mutating 其實(shí)和inout做了同樣的事情,它將隱式的 self 參數(shù)變?yōu)榭勺兊拿0龋⑶腋淖兞诉@個(gè)變量的值
寫時(shí)復(fù)制
在 Swift 標(biāo)準(zhǔn)庫(kù)中酬屉, Array、Dictionary杂靶、 Set 這樣的集合類型是通過(guò)一種叫做寫時(shí)復(fù)制 (copy-on-write) 的技術(shù)實(shí)現(xiàn)的梆惯。
請(qǐng)參考 Swift 寫時(shí)復(fù)制