Swift 1.0文檔翻譯:TimothyYe
Swift 1.0文檔校對:Hawstein Swift 2.0文檔校對及翻譯潤色:Channehtml
PS:以前1.0版中文版看不懂地方在對比英文版後就懂了,仍是以前翻譯的不夠準確啊。此次參與Swift 2.0文檔ARC章節的校對翻譯,順便潤色一下部分翻譯,以便你們更好的理解原文的意思。git
本頁包含內容:github
Swift 使用自動引用計數(ARC)機制來跟蹤和管理你的應用程序的內存。一般狀況下,Swift 的內存管理機制會一直起着做用,你無須本身來考慮內存的管理。ARC 會在類的實例再也不被使用時,自動釋放其佔用的內存。swift
然而,在少數狀況下,ARC 爲了能幫助你管理內存,須要更多的關於你的代碼之間關係的信息。本章描述了這些狀況,而且爲你示範怎樣啓用 ARC 來管理你的應用程序的內存。api
注意:
引用計數僅僅應用於類的實例。結構體和枚舉類型是值類型,不是引用類型,也不是經過引用的方式存儲和傳遞。閉包
當你每次建立一個類的新的實例的時候,ARC 會分配一大塊內存用來儲存實例的信息。內存中會包含實例的類型信息,以及這個實例全部相關屬性的值。函數
此外,當實例再也不被使用時,ARC 釋放實例所佔用的內存,並讓釋放的內存能挪做他用。這確保了再也不被使用的實例,不會一直佔用內存空間。性能
然而,當 ARC 收回和釋放了正在被使用中的實例,該實例的屬性和方法將不能再被訪問和調用。實際上,若是你試圖訪問這個實例,你的應用程序極可能會崩潰。學習
爲了確保使用中的實例不會被銷燬,ARC 會跟蹤和計算每個實例正在被多少屬性,常量和變量所引用。哪怕實例的引用數爲1,ARC都不會銷燬這個實例。ui
爲了使上述成爲可能,不管你將實例賦值給屬性、常量或變量,它們都會建立此實例的強引用。之因此稱之爲「強」引用,是由於它會將實例緊緊的保持住,只要強引用還在,實例是不容許被銷燬的。
下面的例子展現了自動引用計數的工做機制。例子以一個簡單的Person
類開始,並定義了一個叫name
的常量屬性:
class Person {
let name: String
init(name: String) {
self.name = name
print("\(name) is being initialized")
}
deinit {
print("\(name) is being deinitialized")
}
}
複製代碼
Person
類有一個構造函數,此構造函數爲實例的name
屬性賦值,並打印一條消息以代表初始化過程生效。Person
類也擁有一個析構函數,這個析構函數會在實例被銷燬時打印一條消息。
接下來的代碼片斷定義了三個類型爲Person?
的變量,用來按照代碼片斷中的順序,爲新的Person
實例創建多個引用。因爲這些變量是被定義爲可選類型(Person?
,而不是Person
),它們的值會被自動初始化爲nil
,目前還不會引用到Person
類的實例。
var reference1: Person?
var reference2: Person?
var reference3: Person?
複製代碼
如今你能夠建立Person
類的新實例,而且將它賦值給三個變量中的一個:
reference1 = Person(name: "John Appleseed")
// prints "John Appleseed is being initialized」
複製代碼
應當注意到當你調用Person
類的構造函數的時候,"John Appleseed is being initialized」會被打印出來。由此能夠肯定構造函數被執行。
因爲Person
類的新實例被賦值給了reference1
變量,因此reference1
到Person
類的新實例之間創建了一個強引用。正是由於這一個強引用,ARC 會保證Person
實例被保持在內存中不被銷燬。
若是你將同一個Person
實例也賦值給其餘兩個變量,該實例又會多出兩個強引用:
reference2 = reference1
reference3 = reference1
複製代碼
如今這一個Person
實例已經有三個強引用了。
若是你經過給其中兩個變量賦值nil
的方式斷開兩個強引用(包括最早的那個強引用),只留下一個強引用,Person
實例不會被銷燬:
reference1 = nil
reference2 = nil
複製代碼
在你清楚地代表再也不使用這個Person
實例時,即第三個也就是最後一個強引用被斷開時,ARC 會銷燬它。
reference3 = nil
// prints "John Appleseed is being deinitialized"
複製代碼
在上面的例子中,ARC 會跟蹤你所新建立的Person
實例的引用數量,而且會在Person
實例再也不被須要時銷燬它。
然而,咱們可能會寫出一個類實例的強引用數永遠不能變成0的代碼。若是兩個類實例互相持有對方的強引用,於是每一個實例都讓對方一直存在,就是這種狀況。這就是所謂的循環強引用。
你能夠經過定義類之間的關係爲弱引用或無主引用,以替代強引用,從而解決循環強引用的問題。具體的過程在解決類實例之間的循環強引用中有描述。無論怎樣,在你學習怎樣解決循環強引用以前,頗有必要了解一下它是怎樣產生的。
下面展現了一個不經意產生循環強引用的例子。例子定義了兩個類:Person
和Apartment
,用來建模公寓和它其中的居民:
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) is being deinitialized") }
}
複製代碼
class Apartment {
let number: Int
init(number: Int) { self.number = number }
var tenant: Person?
deinit { print("Apartment #\(number) is being deinitialized") }
}
複製代碼
每個Person
實例有一個類型爲String
,名字爲name
的屬性,並有一個可選的初始化爲nil
的apartment
屬性。apartment
屬性是可選的,由於一我的並不老是擁有公寓。
相似的,每一個Apartment
實例有一個叫number
,類型爲Int
的屬性,並有一個可選的初始化爲nil
的tenant
屬性。tenant
屬性是可選的,由於一棟公寓並不老是有居民。
這兩個類都定義了析構函數,用以在類實例被析構的時候輸出信息。這讓你可以知曉Person
和Apartment
的實例是否像預期的那樣被銷燬。
接下來的代碼片斷定義了兩個可選類型的變量john
和number73
,並分別被設定爲下面的Apartment
和Person
的實例。這兩個變量都被初始化爲nil
,這正是可選的優勢:
var john: Person?
var number73: Apartment?
複製代碼
如今你能夠建立特定的Person
和Apartment
實例並將賦值給john
和number73
變量:
john = Person(name: "John Appleseed")
number73 = Apartment(number: 73)
複製代碼
在兩個實例被建立和賦值後,下圖表現了強引用的關係。變量john
如今有一個指向Person
實例的強引用,而變量number73
有一個指向Apartment
實例的強引用:
如今你可以將這兩個實例關聯在一塊兒,這樣人就能有公寓住了,而公寓也有了房客。注意感嘆號是用來展開和訪問可選變量john
和number73
中的實例,這樣實例的屬性才能被賦值:
john!.apartment = number73
number73!.tenant = john
複製代碼
在將兩個實例聯繫在一塊兒以後,強引用的關係如圖所示:
不幸的是,這兩個實例關聯後會產生一個循環強引用。Person
實例如今有了一個指向Apartment
實例的強引用,而Apartment
實例也有了一個指向Person
實例的強引用。所以,當你斷開john
和number73
變量所持有的強引用時,引用計數並不會降爲 0,實例也不會被 ARC 銷燬:
john = nil
number73 = nil
複製代碼
注意,當你把這兩個變量設爲nil
時,沒有任何一個析構函數被調用。循環強引用會一直阻止Person
和Apartment
類實例的銷燬,這就在你的應用程序中形成了內存泄漏。
在你將john
和number73
賦值爲nil
後,強引用關係以下圖:
Person
和Apartment
實例之間的強引用關係保留了下來而且不會被斷開。
Swift 提供了兩種辦法用來解決你在使用類的屬性時所遇到的循環強引用問題:弱引用(weak reference)和無主引用(unowned reference)。
弱引用和無主引用容許循環引用中的一個實例引用另一個實例而不保持強引用。這樣實例可以互相引用而不產生循環強引用。
對於生命週期中會變爲nil
的實例使用弱引用。相反地,對於初始化賦值後不再會被賦值爲nil
的實例,使用無主引用。
弱引用不會對其引用的實例保持強引用,於是不會阻止 ARC 銷燬被引用的實例。這個特性阻止了引用變爲循環強引用。聲明屬性或者變量時,在前面加上weak
關鍵字代表這是一個弱引用。
在實例的生命週期中,若是某些時候引用沒有值,那麼弱引用能夠避免循環強引用。若是引用老是有值,則可使用無主引用,在無主引用中有描述。在上面Apartment
的例子中,一個公寓的生命週期中,有時是沒有「居民」的,所以適合使用弱引用來解決循環強引用。
注意:
弱引用必須被聲明爲變量,代表其值能在運行時被修改。弱引用不能被聲明爲常量。
由於弱引用能夠沒有值,你必須將每個弱引用聲明爲可選類型。在 Swift 中,推薦使用可選類型描述可能沒有值的類型。
由於弱引用不會保持所引用的實例,即便引用存在,實例也有可能被銷燬。所以,ARC 會在引用的實例被銷燬後自動將其賦值爲nil
。你能夠像其餘可選值同樣,檢查弱引用的值是否存在,你將永遠不會訪問已銷燬的實例的引用。
下面的例子跟上面Person
和Apartment
的例子一致,可是有一個重要的區別。這一次,Apartment
的tenant
屬性被聲明爲弱引用:
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) is being deinitialized") }
}
複製代碼
class Apartment {
let number: Int
init(number: Int) { self.number = number }
weak var tenant: Person?
deinit { print("Apartment #\(number) is being deinitialized") }
}
複製代碼
而後跟以前同樣,創建兩個變量(john
和number73
)之間的強引用,並關聯兩個實例:
var john: Person?
var number73: Apartment?
john = Person(name: "John Appleseed")
number73 = Apartment(number: 73)
john!.apartment = number73
number73!.tenant = john
複製代碼
如今,兩個關聯在一塊兒的實例的引用關係以下圖所示:
Person
實例依然保持對Apartment
實例的強引用,可是Apartment
實例只是對Person
實例的弱引用。這意味着當你斷開john
變量所保持的強引用時,再也沒有指向Person
實例的強引用了:
因爲再也沒有指向Person
實例的強引用,該實例會被銷燬:
john = nil
// prints "John Appleseed is being deinitialized"
複製代碼
惟一剩下的指向Apartment
實例的強引用來自於變量number73
。若是你斷開這個強引用,再也沒有指向Apartment
實例的強引用了:
因爲再也沒有指向Apartment
實例的強引用,該實例也會被銷燬:
number73 = nil
// prints "Apartment #73 is being deinitialized"
複製代碼
上面的兩段代碼展現了變量john
和number73
在被賦值爲nil
後,Person
實例和Apartment
實例的析構函數都打印出「銷燬」的信息。這證實了引用循環被打破了。
和弱引用相似,無主引用不會緊緊保持住引用的實例。和弱引用不一樣的是,無主引用是永遠有值的。所以,無主引用老是被定義爲非可選類型(non-optional type)。你能夠在聲明屬性或者變量時,在前面加上關鍵字unowned
表示這是一個無主引用。
因爲無主引用是非可選類型,你不須要在使用它的時候將它展開。無主引用老是能夠被直接訪問。不過 ARC 沒法在實例被銷燬後將無主引用設爲nil
,由於非可選類型的變量不容許被賦值爲nil
。
注意:
若是你試圖在實例被銷燬後,訪問該實例的無主引用,會觸發運行時錯誤。使用無主引用,你必須確保引用始終指向一個未銷燬的實例。
還須要注意的是若是你試圖訪問實例已經被銷燬的無主引用,Swift 確保程序會直接崩潰,而不會發生沒法預期的行爲。因此你應當避免這樣的事情發生。
下面的例子定義了兩個類,Customer
和CreditCard
,模擬了銀行客戶和客戶的信用卡。這兩個類中,每個都將另一個類的實例做爲自身的屬性。這種關係可能會形成循環強引用。
Customer
和CreditCard
之間的關係與前面弱引用例子中Apartment
和Person
的關係略微不一樣。在這個數據模型中,一個客戶可能有或者沒有信用卡,可是一張信用卡老是關聯着一個客戶。爲了表示這種關係,Customer
類有一個可選類型的card
屬性,可是CreditCard
類有一個非可選類型的customer
屬性。
此外,只能經過將一個number
值和customer
實例傳遞給CreditCard
構造函數的方式來建立CreditCard
實例。這樣能夠確保當建立CreditCard
實例時老是有一個customer
實例與之關聯。
因爲信用卡老是關聯着一個客戶,所以將customer
屬性定義爲無主引用,用以免循環強引用:
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit { print("\(name) is being deinitialized") }
}
複製代碼
class CreditCard {
let number: UInt64
unowned let customer: Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { print("Card #\(number) is being deinitialized") }
}
複製代碼
注意:
CreditCard
類的number
屬性被定義爲UInt64
類型而不是Int
類型,以確保number
屬性的存儲量在32位和64位系統上都能足夠容納16位的卡號。
下面的代碼片斷定義了一個叫john
的可選類型Customer
變量,用來保存某個特定客戶的引用。因爲是可選類型,因此變量被初始化爲nil
。
var john: Customer?
複製代碼
如今你能夠建立Customer
類的實例,用它初始化CreditCard
實例,並將新建立的CreditCard
實例賦值爲客戶的card
屬性。
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
複製代碼
在你關聯兩個實例後,它們的引用關係以下圖所示:
Customer
實例持有對CreditCard
實例的強引用,而CreditCard
實例持有對Customer
實例的無主引用。
因爲customer
的無主引用,當你斷開john
變量持有的強引用時,再也沒有指向Customer
實例的強引用了:
因爲再也沒有指向Customer
實例的強引用,該實例被銷燬了。其後,再也沒有指向CreditCard
實例的強引用,該實例也隨之被銷燬了:
john = nil
// prints "John Appleseed is being deinitialized"
// prints "Card #1234567890123456 is being deinitialized"
複製代碼
最後的代碼展現了在john
變量被設爲nil
後Customer
實例和CreditCard
實例的構造函數都打印出了「銷燬」的信息。
###無主引用以及隱式解析可選屬性
上面弱引用和無主引用的例子涵蓋了兩種經常使用的須要打破循環強引用的場景。
Person
和Apartment
的例子展現了兩個屬性的值都容許爲nil
,並會潛在的產生循環強引用。這種場景最適合用弱引用來解決。
Customer
和CreditCard
的例子展現了一個屬性的值容許爲nil
,而另外一個屬性的值不容許爲nil
,這也可能會產生循環強引用。這種場景最適合經過無主引用來解決。
然而,存在着第三種場景,在這種場景中,兩個屬性都必須有值,而且初始化完成後永遠不會爲nil
。在這種場景中,須要一個類使用無主屬性,而另一個類使用隱式解析可選屬性。
這使兩個屬性在初始化完成後能被直接訪問(不須要可選展開),同時避免了循環引用。這一節將爲你展現如何創建這種關係。
下面的例子定義了兩個類,Country
和City
,每一個類將另一個類的實例保存爲屬性。在這個模型中,每一個國家必須有首都,每一個城市必須屬於一個國家。爲了實現這種關係,Country
類擁有一個capitalCity
屬性,而City
類有一個country
屬性:
class Country {
let name: String
var capitalCity: City!
init(name: String, capitalName: String) {
self.name = name
self.capitalCity = City(name: capitalName, country: self)
}
}
複製代碼
class City {
let name: String
unowned let country: Country
init(name: String, country: Country) {
self.name = name
self.country = country
}
}
複製代碼
爲了創建兩個類的依賴關係,City
的構造函數有一個Country
實例的參數,而且將實例保存爲country
屬性。
Country
的構造函數調用了City
的構造函數。然而,只有Country
的實例徹底初始化完後,Country
的構造函數才能把self
傳給City
的構造函數。(在兩段式構造過程當中有具體描述)
爲了知足這種需求,經過在類型結尾處加上感嘆號(City!
)的方式,將Country
的capitalCity
屬性聲明爲隱式解析可選類型的屬性。這表示像其餘可選類型同樣,capitalCity
屬性的默認值爲nil
,可是不須要展開它的值就能訪問它。(在隱式解析可選類型中有描述)
因爲capitalCity
默認值爲nil
,一旦Country
的實例在構造函數中給name
屬性賦值後,整個初始化過程就完成了。這表明一旦name
屬性被賦值後,Country
的構造函數就能引用並傳遞隱式的self
。Country
的構造函數在賦值capitalCity
時,就能將self
做爲參數傳遞給City
的構造函數。
以上的意義在於你能夠經過一條語句同時建立Country
和City
的實例,而不產生循環強引用,而且capitalCity
的屬性能被直接訪問,而不須要經過感嘆號來展開它的可選值:
var country = Country(name: "Canada", capitalName: "Ottawa")
print("\(country.name)'s capital city is called \(country.capitalCity.name)")
// prints "Canada's capital city is called Ottawa"
複製代碼
在上面的例子中,使用隱式解析可選值的意義在於知足了兩個類構造函數的需求。capitalCity
屬性在初始化完成後,能像非可選值同樣使用和存取同時還避免了循環強引用。
前面咱們看到了循環強引用是在兩個類實例屬性互相保持對方的強引用時產生的,還知道了如何用弱引用和無主引用來打破這些循環強引用。
循環強引用還會發生在當你將一個閉包賦值給類實例的某個屬性,而且這個閉包體中又使用了這個類實例。這個閉包體中可能訪問了實例的某個屬性,例如self.someProperty
,或者閉包中調用了實例的某個方法,例如self.someMethod
。這兩種狀況都致使了閉包 「捕獲" self
,從而產生了循環強引用。
循環強引用的產生,是由於閉包和類類似,都是引用類型。當你把一個閉包賦值給某個屬性時,你也把一個引用賦值給了這個閉包。實質上,這跟以前的問題是同樣的-兩個強引用讓彼此一直有效。可是,和兩個類實例不一樣,此次一個是類實例,另外一個是閉包。
Swift 提供了一種優雅的方法來解決這個問題,稱之爲閉包捕獲列表(closuer capture list)。一樣的,在學習如何用閉包捕獲列表破壞循環強引用以前,先來了解一下這裏的循環強引用是如何產生的,這對咱們頗有幫助。
下面的例子爲你展現了當一個閉包引用了self
後是如何產生一個循環強引用的。例子中定義了一個叫HTMLElement
的類,用一種簡單的模型表示 HTML 中的一個單獨的元素:
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: Void -> String = {
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
複製代碼
HTMLElement
類定義了一個name
屬性來表示這個元素的名稱,例如表明段落的"p",或者表明換行的"br"。HTMLElement
還定義了一個可選屬性text
,用來設置和展示 HTML 元素的文本。
除了上面的兩個屬性,HTMLElement
還定義了一個lazy
屬性asHTML
。這個屬性引用了一個將name
和text
組合成 HTML 字符串片斷的閉包。該屬性是Void -> String
類型,或者能夠理解爲「一個沒有參數,返回String
的函數」。
默認狀況下,閉包賦值給了asHTML
屬性,這個閉包返回一個表明 HTML 標籤的字符串。若是text
值存在,該標籤就包含可選值text
;若是text
不存在,該標籤就不包含文本。對於段落元素,根據text
是"some text"
仍是nil
,閉包會返回"<p>some text</p>
"或者"<p />
"。
能夠像實例方法那樣去命名、使用asHTML
屬性。然而,因爲asHTML
是閉包而不是實例方法,若是你想改變特定元素的 HTML 處理的話,能夠用自定義的閉包來取代默認值。
注意:
asHTML
聲明爲lazy
屬性,由於只有當元素確實須要處理爲HTML輸出的字符串時,才須要使用asHTML
。也就是說,在默認的閉包中可使用self
,由於只有當初始化完成以及self
確實存在後,才能訪問lazy
屬性。
HTMLElement
類只提供一個構造函數,經過name
和text
(若是有的話)參數來初始化一個元素。該類也定義了一個析構函數,當HTMLElement
實例被銷燬時,打印一條消息。
下面的代碼展現瞭如何用HTMLElement
類建立實例並打印消息。
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
// prints"hello, world"
複製代碼
注意:
上面的paragraph
變量定義爲可選HTMLElement
,所以咱們能夠賦值nil
給它來演示循環強引用。
不幸的是,上面寫的HTMLElement
類產生了類實例和asHTML
默認值的閉包之間的循環強引用。循環強引用以下圖所示:
實例的asHTML
屬性持有閉包的強引用。可是,閉包在其閉包體內使用了self
(引用了self.name
和self.text
),所以閉包捕獲了self
,這意味着閉包又反過來持有了HTMLElement
實例的強引用。這樣兩個對象就產生了循環強引用。(更多關於閉包捕獲值的信息,請參考值捕獲)。
注意:
雖然閉包屢次使用了self
,它只捕獲HTMLElement
實例的一個強引用。
若是設置paragraph
變量爲nil
,打破它持有的HTMLElement
實例的強引用,HTMLElement
實例和它的閉包都不會被銷燬,也是由於循環強引用:
paragraph = nil
複製代碼
注意HTMLElementdeinitializer
中的消息並無被打印,證實了HTMLElement
實例並無被銷燬。
在定義閉包時同時定義捕獲列表做爲閉包的一部分,經過這種方式能夠解決閉包和類實例之間的循環強引用。捕獲列表定義了閉包體內捕獲一個或者多個引用類型的規則。跟解決兩個類實例間的循環強引用同樣,聲明每一個捕獲的引用爲弱引用或無主引用,而不是強引用。應當根據代碼關係來決定使用弱引用仍是無主引用。
注意:
Swift 有以下要求:只要在閉包內使用self
的成員,就要用self.someProperty
或者self.someMethod
(而不僅是someProperty
或someMethod
)。這提醒你可能會一不當心就捕獲了self
。
###定義捕獲列表
捕獲列表中的每一項都由一對元素組成,一個元素是weak
或unowned
關鍵字,另外一個元素是類實例的引用(如self
)或初始化過的變量(如delegate = self.delegate!
)。這些項在方括號中用逗號分開。
若是閉包有參數列表和返回類型,把捕獲列表放在它們前面:
lazy var someClosure: (Int, String) -> String = {
[unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
// closure body goes here
}
複製代碼
若是閉包沒有指明參數列表或者返回類型,即它們會經過上下文推斷,那麼能夠把捕獲列表和關鍵字in
放在閉包最開始的地方:
lazy var someClosure: Void -> String = {
[unowned self, weak delegate = self.delegate!] in
// closure body goes here
}
複製代碼
###弱引用和無主引用
在閉包和捕獲的實例老是互相引用時而且老是同時銷燬時,將閉包內的捕獲定義爲無主引用。
相反的,在被捕獲的引用可能會變爲nil
時,將閉包內的捕獲定義爲弱引用。弱引用老是可選類型,而且當引用的實例被銷燬後,弱引用的值會自動置爲nil
。這使咱們能夠在閉包體內檢查它們是否存在。
注意:
若是被捕獲的引用絕對不會變爲nil
,應該用無主引用,而不是弱引用。
前面的HTMLElement
例子中,無主引用是正確的解決循環強引用的方法。這樣編寫HTMLElement
類來避免循環強引用:
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: Void -> String = {
[unowned self] in
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
複製代碼
上面的HTMLElement
實現和以前的實現一致,除了在asHTML
閉包中多了一個捕獲列表。這裏,捕獲列表是[unowned self]
,表示「用無主引用而不是強引用來捕獲self
」。
和以前同樣,咱們能夠建立並打印HTMLElement
實例:
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
// prints "<p>hello, world</p>"
複製代碼
使用捕獲列表後引用關係以下圖所示:
這一次,閉包以無主引用的形式捕獲self
,並不會持有HTMLElement
實例的強引用。若是將paragraph
賦值爲nil
,HTMLElement
實例將會被銷燬,並能看到它的析構函數打印出的消息。
paragraph = nil
// prints "p is being deinitialized"
複製代碼