十九
. swift
記憶體管理
1. swift
提供了強大的記憶體管理機制:
swift
通過自動引用計數(
arc)可以很好地管理物件的**。大部分時候,程式無須關心
swift
物件的**,但在某些特殊情況下,
swift
要求程式設計師進行一些記憶體管理的處理。
2. 只有引用型別變數所引用的物件才需要使用引用計數進行管理,對於列舉、結構體,他們是值型別,因此不需要使用引用計數進行管理。
3. arc
是一種非常優秀的記憶體管理技術,它的實現思路非常簡單
:當程式在記憶體中建立乙個物件之後,
arc將會自動完成兩件事情:(1
)arc
自動統計該物件被多少個引用變數所引用,這個值就被稱為引用計數。簡單地說,
arc相當於為每個物件額外增加了乙個
int型別的屬性,該屬性總能正確地記錄有多少個引用變數在引用該物件。(2
)每當乙個物件的引用計數為0時,
arc會自動**該物件。
4. 沒有必要去了解
arc到底是如何統計的,使用
arc之後,程式甚至不允許直接訪問物件的引用計數。
5. 強引用:大部分時候,
arc可以很好地處理程式中物件的記憶體**,但如果兩個物件之間存在雙向關聯:兩個物件都使用儲存屬性互相引用對方的時候,此時兩個物件的引用計數都等於
1,但它們實際上並沒有被真正的引用變數所引用,但
arc也無法**它們。如果沒有特殊說明,
swift
中的引用變數都是強引用,此時兩個物件形成了強引用迴圈。
6. 為了解決上面的強引用迴圈,必須有一方做出「讓步
」,允許對方先釋放,這樣問題就解決了。
swift
為解決強引用迴圈提供了兩種解決機制:弱引用和無主引用。 7.
使用弱引用解決強引用迴圈:弱應用不會增加對方的引用計數,因此不會阻止
arc**被引用的例項,這樣就可以避免形成強引用迴圈。在定義屬性的
var關鍵字前面使用
weak
關鍵字即可定義弱引用。
8. 弱應用變數要求該應用變數
***必須
***
允許被設為
nil,也就是弱應用是可以沒有值的,所以推薦使用可選型別來定義弱引用屬性。
9. 弱應用只能宣告為變數型別,因為該屬性在允許期間內值有可能發生改變,因此弱引用決不能宣告為常量。
10.
由於弱引用不會持有例項,因此即使弱引用存在,
arc也可能會銷毀例項,並將弱引用變數賦值為
nil,所以程式可以像檢查其他可選型別的變數一樣檢查弱引用是否存在,這樣就永遠不會出現呼叫了被銷毀例項的情形。
11.
使用弱引用非常簡單,只要將相互引用的兩方中的任意一方的關聯屬性使用
weak
修飾即可,舉個栗子:
class student
deinit}
12.
使用無主引用解決強引用迴圈:無主引用也不會增加對方的引用計數。無主引用與弱引用的區別是:無主引用不允許接受
nil(即應該始終有值),因此無主引用
只能定義為非可選
型別。在定義屬性的
var或
let關鍵字之前新增
unowned
關鍵字即可定義無主引用。
13.
因為無主引用是非可選型別的,因此當使用使用無主引用時不需要強制解析,可以直接訪問。但由於非可選型別的屬性不能為
nil,因此當
arc**了引用變數屬性所引用的例項之後,
arc無法將該引用變數屬性賦值為
nil
14.
當無主引用所引用的物件被銷毀之後,無主引用變數並沒有被賦值為
nil,因此程式無法準確判斷無主引用所引用的物件是否已經被銷毀。如果程式試圖通過無主引用去呼叫被銷毀的例項,將會導致執行時錯誤。
15.
弱引用和無主引用的例項覆蓋了兩種常用的場景:(1
)如果兩個實體中記錄關聯實體的屬性都可能是
nil,並有可能產生強引用迴圈,此時適合使用弱引用。(2
)如果兩個實體中記錄關聯實體的屬性乙個是
nil,另乙個不能是
nil,並有可能產生強引用迴圈,此時適合使用無主引用。
還有一種情況:兩個屬性都必須有值,且初始化後都不會為
nil,並有可能產生強引用迴圈,此時要求乙個類使用無主引用,另乙個類使用隱式可選屬性。
16.
閉包與物件的強引用迴圈:如果累的某個屬性不是普通型別,而是函式型別,那麼這個屬性就有可能返回乙個閉包。閉包會捕獲傳入其中的引用變數
----
如果程式將該物件本身傳入了閉包,那麼閉包本身就會捕獲該物件,於是該物件就持有了閉包屬性;反過來,閉包也持有了該物件,這樣物件和閉包也形成了強引用迴圈。
17.
解決閉包與物件之間的強引用迴圈時,到底採用弱引用還是無主引用,也需要根據實際場景來選擇。當閉包和捕獲的例項總是相互引用,且總是同時銷毀時,應該將閉包內捕獲的例項定義為無主引用。相反的,當閉包捕獲的引用變數有可能是
nil時,將閉包捕獲的引用變數定義為弱引用。弱引用必須是可選型別,當被引用的例項被銷毀後,弱引用的變數會自動被賦值為
nil。
18. swift
通過為閉包定義捕獲列表來指定捕獲變數應該採用弱引用,還是採用無主引用。語法格式如下:
如果閉包無須定義形參、返回值型別,則該閉包語法格式為:
可以看出,捕獲列表總是定義在閉包的第一行,且使用方括號括起來,每個捕獲變數都需要
unowned
或者weak
修飾。
19.
舉個栗子:
class student
init(name : string, age : int)
//定義析構器
deinit}
var stu : student? = student(name : "xiaoming", age : 12)
var info : (() -> string)? = stu!.stuinfo
stu = nil
info = nil
由於無主引用不會增加對方的引用計數,因此在程式中
student
物件的計數為
0,於是
arc將會先釋放記憶體中的
student
物件。當
student
物件被釋放之後,
student
的屬性就不復存在了,此時
student
物件也就不再引用記憶體中的閉包了,於是閉包的引用計數也變為0,
arc會釋放閉包。
每天學一點Swift 物件導向下 二
二 類的構造和析構 1.通過整合後,子類中不僅有父類中的儲存屬性,還有子類自己的儲存屬性。子類中的所有儲存屬性都必須在構造器中設定初始值,因此類的構造過程會相對比較複雜。2.與構造器相反的是,swift 允許為類 列舉 結構體不允許 定義析構器 在例項臨近銷毀之前,系統會自動呼叫該例項的析構器 例項...
每天學一點Swift 物件導向下 七
十一 使用協議作為型別 1.協議也相當於一種型別,與列舉 結構體 類相比,協議相當於一種抽象的型別,它被徹底抽象成只定義規範,不負責實現。因此定義協議之後,就可以像列舉 結構體 類那樣當作型別來使用,只是協議不能直接用於建立例項,協議可以做如下方面的用途 1 可使用協議宣告變數 2 可使用協議作為函...
每天學一點Swift 物件導向上 十二
十四 構造器 1.構造器用於完成例項的構造過程。這個過程包括為例項中的每個儲存屬性設定初始值和執行必要的準備和初始化任務。2.swift 的構造器本質上就是乙個或多個名為 init 的函式 不允許使用 func 關鍵字 3.構造器的主要作用就是完成例項中每個類 結構體中例項儲存屬性 列舉不能定義例項...