Swift筆記之變數講解
Swift 是一種支援多程式設計正規化和編譯式的開源程式語言,蘋果於2014年WWDC(蘋果開發者大會)釋出,用於開發 iOS,OS X 和 watchOS 應用程式。 |
Swift 使用自動引用計數(ARC)這一機制來跟蹤和管理應用程式的記憶體
通常情況下我們不需要去手動釋放記憶體,因為 ARC 會在類的例項不再被使用時,自動釋放其佔用的記憶體。
但在有些時候我們還是需要在程式碼中實現記憶體管理。
1.當每次使用 init() 方法建立一個類的新的例項的時候,ARC 會分配一大塊記憶體用來儲存例項的資訊。
2.記憶體中會包含例項的型別資訊,以及這個例項所有相關屬性的值。
3.當例項不再被使用時,ARC 釋放例項所佔用的記憶體,並讓釋放的記憶體能挪作他用。
4.為了確保使用中的例項不會被銷燬,ARC 會跟蹤和計算每一個例項正在被多少屬性,常量和變數所引用。
5.例項賦值給屬性、常量或變數,它們都會建立此例項的強引用,只要強引用還在,例項是不允許被銷燬的。
class Person { let name: String init(name: String) { self.name = name print("\(name) 開始初始化") } deinit { print("\(name) 被析構") } } // 值會被自動初始化為nil,目前還不會引用到Person類的例項 var reference1: Person? var reference2: Person? var reference3: Person? // 建立Person類的新例項 reference1 = Person(name: "Runoob") //賦值給其他兩個變數,該例項又會多出兩個強引用 reference2 = reference1 reference3 = reference1 //斷開第一個強引用 reference1 = nil //斷開第二個強引用 reference2 = nil //斷開第三個強引用,並呼叫解構函式 reference3 = nil
以上程式執行輸出結果為:
Runoob 開始初始化 Runoob 被析構
在上面的例子中,ARC 會跟蹤你所新建立的 Person 例項的引用數量,並且會在 Person 例項不再被需要時銷燬它。
然而,我們可能會寫出這樣的程式碼,一個類永遠不會有0個強引用。這種情況發生在兩個類例項互相保持對方的強引用,並讓對方不被銷燬。這就是所謂的迴圈強引用。
下面展示了一個不經意產生迴圈強引用的例子。例子定義了兩個類:Person和Apartment,用來建模公寓和它其中的居民:
class Person { let name: String init(name: String) { self.name = name } var apartment: Apartment? deinit { print("\(name) 被析構") } } class Apartment { let number: Int init(number: Int) { self.number = number } var tenant: Person? deinit { print("Apartment #\(number) 被析構") } } // 兩個變數都被初始化為nil var runoob: Person? var number73: Apartment? // 賦值 runoob = Person(name: "Runoob") number73 = Apartment(number: 73) // 意感嘆號是用來展開和訪問可選變數 runoob 和 number73 中的例項 // 迴圈強引用被建立 runoob!.apartment = number73 number73!.tenant = runoob // 斷開 runoob 和 number73 變數所持有的強引用時,引用計數並不會降為 0,例項也不會被 ARC 銷燬 // 注意,當你把這兩個變數設為nil時,沒有任何一個解構函式被呼叫。 // 強引用迴圈阻止了Person和Apartment類例項的銷燬,並在你的應用程式中造成了記憶體洩漏 runoob = nil number73 = nil
Swift 提供了兩種辦法用來解決你在使用類的屬性時所遇到的迴圈強引用問題:
1.弱引用
2.無主引用
弱引用和無主引用允許迴圈引用中的一個例項引用另外一個例項而不保持強引用。這樣例項能夠互相引用而不產生迴圈強引用。
對於生命週期中會變為nil的例項使用弱引用。相反的,對於初始化賦值後再也不會被賦值為nil的例項,使用無主引用。
class Module { let name: String init(name: String) { self.name = name } var sub: SubModule? deinit { print("\(name) 主模組") } } class SubModule { let number: Int init(number: Int) { self.number = number } weak var topic: Module? deinit { print("子模組 topic 數為 \(number)") } } var toc: Module? var list: SubModule? toc = Module(name: "ARC") list = SubModule(number: 4) toc!.sub = list list!.topic = toc toc = nil list = nil
以上程式執行輸出結果為:
ARC 主模組 子模組 topic 數為 4
class Student { let name: String var section: Marks? init(name: String) { self.name = name } deinit { print("\(name)") } } class Marks { let marks: Int unowned let stname: Student init(marks: Int, stname: Student) { self.marks = marks self.stname = stname } deinit { print("學生的分數為 \(marks)") } } var module: Student? module = Student(name: "ARC") module!.section = Marks(marks: 98, stname: module!) module = nil
以上程式執行輸出結果為:
ARC 學生的分數為 98
迴圈強引用還會發生在當你將一個閉包賦值給類例項的某個屬性,並且這個閉包體中又使用了例項。這個閉包體中可能訪問了例項的某個屬性,例如self.someProperty,或者閉包中呼叫了例項的某個方法,例如self.someMethod。這兩種情況都導致了閉包 "捕獲" self,從而產生了迴圈強引用。
下面的例子為你展示了當一個閉包引用了self後是如何產生一個迴圈強引用的。例子中定義了一個叫HTMLElement的類,用一種簡單的模型表示 HTML 中的一個單獨的元素:
class HTMLElement { let name: String let text: String? lazy var asHTML: () -> String = { if let text = self.text { return "<\(self.name)>\(text)" } else { return "<\(self.name) />" } } init(name: String, text: String? = nil) { self.name = name self.text = text } deinit { print("\(name) is being deinitialized") } } // 建立例項並列印資訊 var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world") print(paragraph!.asHTML())
HTMLElement 類產生了類例項和 asHTML 預設值的閉包之間的迴圈強引用。
例項的 asHTML 屬性持有閉包的強引用。但是,閉包在其閉包體內使用了self(引用了self.name和self.text),因此閉包捕獲了self,這意味著閉包又反過來持有了HTMLElement例項的強引用。這樣兩個物件就產生了迴圈強引用。
解決閉包引起的迴圈強引用:在定義閉包時同時定義捕獲列表作為閉包的一部分,透過這種方式可以解決閉包和類例項之間的迴圈強引用。
當閉包和捕獲的例項總是互相引用時並且總是同時銷燬時,將閉包內的捕獲定義為無主引用。
相反的,當捕獲引用有時可能會是nil時,將閉包內的捕獲定義為弱引用。
如果捕獲的引用絕對不會置為nil,應該用無主引用,而不是弱引用。
前面的HTMLElement例子中,無主引用是正確的解決迴圈強引用的方法。這樣編寫HTMLElement類來避免迴圈強引用:
class HTMLElement { let name: String let text: String? lazy var asHTML: () -> String = { [unowned self] in if let text = self.text { return "<\(self.name)>\(text)" } else { return "<\(self.name) />" } } init(name: String, text: String? = nil) { self.name = name self.text = text } deinit { print("\(name) 被析構") } } //建立並列印HTMLElement例項 var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world") print(paragraph!.asHTML()) // HTMLElement例項將會被銷燬,並能看到它的解構函式列印出的訊息 paragraph = nil
以上程式執行輸出結果為:
hello, world p 被析構
原文地址:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31559985/viewspace-2710164/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Swift學習筆記(三十二)——可變引數Swift筆記
- java乾貨筆記之變數4Java筆記變數
- Swift學習筆記(三十三)——常量引數,變數引數和inout引數Swift筆記變數
- C語言學習筆記之變數C語言筆記變數
- Docker筆記五之Docker系統變數Docker筆記變數
- Swift 常量講解Swift
- 《從零開始學Swift》學習筆記 (Day 58)—— Swift編碼規範之變數或常量宣告規範Swift筆記變數
- Nginx變數詳解(學習筆記十九)Nginx變數筆記
- swift 學習筆記之陣列Swift筆記陣列
- CUDA學習筆記之變數限制符筆記變數
- Swift 變數 (三)Swift變數
- Swift 字元(Character)講解Swift字元
- Swift 字面量講解Swift
- 【筆記】遊標變數筆記變數
- REDHAT LINUX學習筆記之環境變數RedhatLinux筆記變數
- Swift: 可變引數Swift
- swift dispatch 筆記Swift筆記
- JavaScript筆記3_變數JavaScript筆記變數
- Tensorflow學習筆記: 變數及共享變數筆記變數
- Swift學習筆記之-Implicitly unwrapped optionalsSwift筆記APP
- 《從零開始學Swift》學習筆記(Day 8)——小小常量、變數大作用Swift筆記變數
- IOS筆記之可變字串iOS筆記字串
- JavaScript ECMAScript 6 筆記 -2 《變數的解構賦值》JavaScript筆記變數賦值
- Swift(一)常量和變數Swift變數
- Mysql系列第十六講 變數詳解MySql變數
- 14_Linux環境變數講解Linux變數
- Swift 條件語句講解Swift
- Swift的訪問控制講解Swift
- 筆記-go反射操作私有變數筆記Go反射變數
- Python學習筆記 - 變數Python筆記變數
- javascript學習筆記,二、變數JavaScript筆記變數
- 數論筆記:快速傅立葉變換筆記
- 5.3_前端筆記-js變數前端筆記JS變數
- swift 基礎筆記Swift筆記
- swift學習筆記《2》-swift語法Swift筆記
- Python 3 學習筆記之——變數作用域、模組和包Python筆記變數
- Python學習筆記|Python之內建變數__name__Python筆記變數
- [go 學習筆記] 二、變數、常量Go筆記變數