精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Swift內存管理機制深度解析

譯文
移動開發 iOS
作為一種現代化高級編程語言,Swift為您的應用程序中的分配、釋放等內存管理需求提供強有力的支持。它使用的是一種稱為自動化引用計數(ARC)的技術。通過本文的學習,你將通過以下內容進一步提升你的Swift開發中的ARC編程水平。

【51CTO.com快譯】

簡介

作為一種現代化高級編程語言,Swift為您的應用程序中的分配、釋放等內存管理需求提供強有力的支持。它使用的是一種稱為自動化引用計數(ARC)的技術。通過本文的學習,你將通過以下內容進一步提升你的Swift開發中的ARC編程水平:

  • 了解ARC的工作原理。
  • 何謂引用循環以及如何消除這種循環。
  • 通過一個實例展示引用循環,并通過最新的Xcode可視化工具了解如何檢測這種循環。
  • 如何處理值類型和引用類型混合應用情形。

入門

打開Xcode并單擊命令「File\New\Playground…」。然后,選擇iOS平臺,并將其命名為「MemoryManagement」,然后選擇【Next】命令。最后,把工程保存到你想要存儲的目標位置,然后刪除其中的樣板代碼并保存工程。

接下來,將下面的代碼添加到您的工程文件中:

  1. class User { 
  2.  
  3.   var name: String  
  4.  
  5.   init(name: String) { 
  6.  
  7.     self.name = name 
  8.  
  9.     print("User \(name) is initialized") 
  10.  
  11.   } 
  12.  
  13.   deinit { 
  14.  
  15.     print("User \(name) is being deallocated") 
  16.  
  17.   } 
  18.  
  19.  
  20. let user1 = User(name: "John") 

這段代碼定義了一個類User,并創建它的一個實例。該類有一個屬性是name,定義了一個init方法(剛好在內存分配之后進行調用)和一個deinit方法(剛好在內存回收后調用)。打印語句print用于及時輸出你想看到的所發生的事情。

從輸出結果中,你會發現在側邊欄中顯示出“User John is initialized\n”;此信息是通過在初始化方法init中的打印語句print輸出的。但是,你會發現,deinit方法內的print語句永遠不會被調用。這意味著,對象永遠不會被析構。當然,這也就意味著它永遠不會被釋放。這是因為,它被初始化的范圍永遠不會關閉——工程本身永遠不會走出這個范圍——因此,對象不會從內存中刪除。

現在,我們改變上面的初始化方法,像如下這樣:

  1. do { 
  2.  
  3.   let user1 = User(name: "John") 
  4.  

此語句創建了一個范圍,此圍繞包含了user1對象的初始化。于是,在作用域結束后,我們希望user1對象會被釋放。

現在,你看到對應于初始化和析構方法中的兩個print語句都在側邊欄中輸出了內容。這表明,該對象在上面定義的作用域結束后,也就是恰好在它被從內存中刪除之前被析構。

歸納起來,一個Swift對象的生命周期包括五個階段:

1. 分配(從堆棧或堆中分配內存)

2. 初始化(init代碼運行)

3. 使用(使用對象)

4. 析構(deinit代碼運行)

5. 釋放(內存又回到了堆棧或堆)

雖然沒有直接的鉤子技術埋伏到內存分配和內存回收中,但是您可以在init和deinit方法中 使用print語句作為代理手段來監控上述過程。注意,盡管上面過程4和5中的釋放和析構兩個方法常常交替使用,但實際上它們是在一個對象的生命周期中的兩個不同的階段。

引用計數是當不再需要對象時被釋放的機制。這里的問題是:“你何時可以肯定未來不會需要這個對象?”通過保持一個使用次數的統計計數,即“引用計數”即可實現這一管理目的。引用計數存儲于每一個對象實例內部。

上面的計數能夠確定,有多少“東西”引用了對象。當一個對象的引用計數降為零時,也就是說對象的客戶端不再存在;于是,對象被析構和解除內存分配;請參考下圖示意。 

當你初始化User對象時,開始時該對象的引用計數為1,因為常量user1引用該對象。在do語句塊的結束,user1超出范圍,計數減1,并且引用計數遞減到零。這樣一來,user1被析構,并且隨后取消內存分配。

引用循環

在大多數情況下,ARC就像一個魔法一樣起作用。作為開發人員,您通常不必擔心內存泄露,例如不必擔心未使用的對象是否還存活于內存中。

但事情并不總是一帆風順!內存泄漏也可能發生!

泄漏是怎樣發生的?讓我們設想有這樣的情況,某兩個對象不再需要使用它們,但它們各自引用了對方。既然每一個對象都有一個非零的引用計數;那么,這兩個對象的釋放就永遠不會發生。 

這就是所謂的強引用循環。它愚弄了ARC,并防止被從內存中清理掉。正如你所看到的,在最后的引用計數并不為零,因而object1和object2是永遠不會釋放的,即使不再需要它們。

為了觀察這種情況的真實例子,請添加以下代碼到User類的定義之后,且正好在現有的do語句之前:

  1. class Phone { 
  2.  
  3.   let model: String 
  4.  
  5.   var owner: User? 
  6.  
  7.   init(model: String) { 
  8.  
  9.     self.model = model 
  10.  
  11.     print("Phone \(model) is initialized") 
  12.  
  13.   } 
  14.  
  15.   
  16.  
  17.   deinit { 
  18.  
  19.     print("Phone \(model) is being deallocated") 
  20.  
  21.   } 
  22.  

然后,把do語句塊修改成如下這樣:

  1. do {  
  2.  
  3.   let user1 = User(name: "John") 
  4.  
  5.   let iPhone = Phone(model: "iPhone 6s Plus") 
  6.  

這將增加了一個名為Phone的新類,并創建此新類的一個實例。

這個新的類是相當簡單的:擁有兩個屬性,一個用于模型存儲和一個用于擁有者,還有一個初始化方法init和析構方法deinit。其中,owner屬性是可選的,因為Phone可以不需要User而存在。

接下來,將下面的代碼添加到User類中,正好位于name屬性后面:

  1. private(set) var phones: [Phone] = [] 
  2.  
  3. func add(phone: Phone) { 
  4.  
  5.   phones.append(phone) 
  6.  
  7.   phone.owner = self 
  8.  

這部分代碼將增加一個phones數組屬性來保存一個用戶所擁有的所有電話號碼。而且,這個setter方法是私有的,這樣客戶端會被強制使用add(phone:)方法。此方法可確保當你添加新號碼時owner設置正確。

目前,如你可以在側邊欄中看到的,無論是Phone還是User對象都會按預期釋放。

但現在,你如果把do語句塊修改成如下這樣:

  1. do {  
  2.  
  3.   let user1 = User(name: "John") 
  4.  
  5.   let iPhone = Phone(model: "iPhone 6s Plus") 
  6.  
  7.   user1.add(phone: iPhone) 
  8.  

在這里,你把iPhone添加到user1。這會自動將iPhone的owner設置為user1。在這兩個對象之間的一個強引用循環防止ARC重新分配它們。這樣一來,無論是user1還是iPhone從未被釋放。 

弱引用

為了打破引用循環,您可以將引用計數的對象之間的關系指定為weak。除非另有說明,所有引用都是強引用。相比之下,弱引用并不會增加對象的強引用計數。

換句話說,弱引用并不參加對象的生命周期管理。此外,弱引用總是被聲明為optional類型。這意味著,當引用計數變為零時,引用可被自動設置為nil。 

在上圖中,虛線箭頭表示弱引用。注意,圖中的object1的引用計數是1,因為變量variable1引用了它。Object2的引用計數為2,因為variable2和object1都引用了它。但是,object2弱引用object1,這意味著它不會影響object1的強引用計數。

當兩個變量(即變量variable1和變量variable2)銷毀后,object1的引用計數為零并將調用deinit。這將消除對object2的強引用;當然,隨后object2也被析構。

現在,請再打開上面的示例工程,通過使owner成為弱引用,從而打破User和Phone間的引用循環,代碼如下所示:

  1. class Phone { 
  2.  
  3.   weak var owner: User? 
  4.  
  5.   // other code... 
  6.  

相應的圖示如下: 

現在,user1和iphone這兩個變量在do語句塊的最后都能夠正確釋放內存。你可以從側邊欄的輸出結果中觀察到這一點。

無主引用

Swift語言中還引入了另一種不增加引用計數的引用修飾符:unowned。

那么,unowned和weak引用之間的區別是什么?弱引用始終是可選的,并且當引用對象析構時自動變為nil。這就是為什么為了使你的代碼進行編譯(因為變量需要改變)而必須把弱屬性定義為可選的var類型的原因。

無主引用,相比之下,絕不是可有可無的類型。如果您嘗試訪問一個引用了一個析構對象的無主屬性,你會觸發一個運行時錯誤,請參考下圖。

接下來,我們來實際使用一下unowned修飾符。在上面do塊之前添加一個新類CarrierSubscription,如下所示:

  1. class CarrierSubscription { 
  2.  
  3.   let name: String 
  4.  
  5.   let countryCode: String 
  6.  
  7.   let number: String 
  8.  
  9.   let user: User 
  10.  
  11.   
  12.  
  13.   init(name: String, countryCode: String, number: String, user: User) { 
  14.  
  15.     self.name = name 
  16.  
  17.     self.countryCode = countryCode 
  18.  
  19.     self.number = number 
  20.  
  21.     self.user = user 
  22.  
  23.   
  24.  
  25.     print("CarrierSubscription \(name) is initialized") 
  26.  
  27.   } 
  28.  
  29.   
  30.  
  31.   deinit { 
  32.  
  33.     print("CarrierSubscription \(name) is being deallocated") 
  34.  
  35.   } 
  36.  

CarrierSubscription具有四個屬性:訂閱名name,國家代碼countryCode,電話號碼phone和一個到User對象的引用。

接下來,將以下語句添加到User類中,正好在name屬性的定義后:

var subscriptions: [CarrierSubscription] = []

這將增加一個subscriptions屬性,此屬性中存儲一組CarrierSubscrition對象。

此外,將以下代碼添加到Phone類的頂部,正好位于owner屬性的后面:

  1. var carrierSubscription: CarrierSubscription?  
  2.  
  3. func provision(carrierSubscription: CarrierSubscription) { 
  4.  
  5.   self.carrierSubscription = carrierSubscription 
  6.  
  7. }  
  8.  
  9. func decommission() { 
  10.  
  11.   self.carrierSubscription = nil 
  12.  

這將增加一個可選的CarrierSubscription屬性和兩個新的函數。

接下來,添加以下代碼到CarrierSubscription類的初始化方法init中,正好位于打印語句之前:

user.subscriptions.append(self)

這將確保CarrierSubscription被添加到用戶的訂閱數組中。

最后,修改do語句塊,如下所示:

  1. do {  
  2.  
  3.   let user1 = User(name: "John") 
  4.  
  5.   let iPhone = Phone(model: "iPhone 6s Plus") 
  6.  
  7.   user1.add(phone: iPhone) 
  8.  
  9.   let subscription1 = CarrierSubscription(name: "TelBel", countryCode: "0032", number: "31415926", user: user1) 
  10.  
  11.   iPhone.provision(carrierSubscription: subscription1) 
  12.  

請注意觀察在側邊欄的打印結果。同樣,你又看到一個引用循環:user1,iPhone或subscription1在最后都沒有被釋放。你能找到問題出在哪里嗎? 

無論是從user1到subscription1的引用,還是從subscription1到user1的引用都應當是無主引用,從而打破這種循環。現在的問題是:這兩個應選擇哪一種?要解決這個問題,需要你有一點關于域(domain)的知識作為幫助。

用戶擁有一個訂閱,而訂閱并不擁有用戶。此外,沒有擁有它的用戶的CarrierSubscription是沒有任何存在意義的。這就是為什么你在最開始的位置把它聲明為一個不可改變的let類型屬性的原因。

由于沒有CarrierSubscription的用戶可以存在,但沒有用戶的CarrierSubscription沒有存在必要;因此,user引用應當是無主類型(unowned)的。

接下來,把CarrierSubscription的user屬性添加上unowned修飾符,像下面這樣:

  1. class CarrierSubscription { 
  2.  
  3.   let name: String 
  4.  
  5.   let countryCode: String 
  6.  
  7.   let number: String 
  8.  
  9.   unowned let user: User 
  10.  
  11.   // Other code... 
  12.  

這樣一來,就可以打破引用循環,從而讓每一個對象都可以釋放內存分配。

閉包的引用循環問題

當屬性相互引用時就會發生對象引用循環情況。類似于對象,閉包也是引用類型,并因此也可能導致循環引用。但是,閉包能夠捕獲它們所操作的對象。

例如,如果一個閉包被賦值給一個類的屬性,而該閉包使用了同一類的實例屬性,則就出現了一個引用循環。換句話說,在對象中通過保存的屬性擁有了到閉包的引用;而閉包也通過self關鍵字保持著到對象的引用。請參考下圖進一步理解。

 

添加下面代碼到CarrierSubscription定義,也就是在user屬性的定義之后的位置:

  1. lazy var completePhoneNumber: () -> String = { 
  2.  
  3.   self.countryCode + " " + self.number 
  4.  

此閉合計算并返回一個完整的電話號碼。注意,這個屬性是使用lazy關鍵字聲明的;這意味著,直到第一次使用它時它才會被分配。這是必要的,因為它要使用self.countryCode和self.number;而直到初始化運行后這才能夠可用。

現在,請添加下面一行代碼到do語句塊的結尾:

  1. print(subscription1.completePhoneNumber()) 

從上面輸出中你會發現,user1和iPhone兩個對象都能夠成功地回收內存分配,但CarrierSubscription卻不能,這是由于在對象和閉包之間存在強引用循環所致。 

Swift提供了一種簡單而優雅的方式來打破強引用循環中的閉包。方法是:我們只要聲明一個捕獲列表,并在此列表中定義它所捕獲的閉包和對象之間的關系。

為了說明捕獲列表是如何工作的,不妨考慮下面的代碼:

  1. var x = 5 
  2.  
  3. var y = 5  
  4.  
  5. let someClosure = { [x] in 
  6.  
  7.   print("\(x), \(y)") 
  8.  
  9. }  
  10.  
  11. x = 6 
  12.  
  13. y = 6  
  14.  
  15. someClosure()        // Prints 5, 6 
  16.  
  17. print("\(x), \(y)")  // Prints 6, 6 

在上面代碼中,變量x是在捕獲列表中;因此,在閉包定義點就創建了x的一個拷貝。這稱為通過值捕獲。另一方面,y沒有定義于捕獲列表中,因此被以引用方式捕獲。這意味著,在閉合運行時,y的值將是對應于此時的任何可能的取值,而不是對應于捕獲點處原來的值。

因此,捕捉列表用于在閉包內部定義弱引用對象或無主引用對象之間的關系。在上述例子中,unowned引用就是一個不錯的選擇,因為在CarrierSubscription的實例消失后閉包是不可能存在的。

現在,請把CarrierSubscription的completePhoneNumber閉包更改成如下樣子:

  1. lazy var completePhoneNumber: () -> String = { 
  2.  
  3.   [unowned self] in 
  4.  
  5.   return self.countryCode + " " + self.number 
  6.  

這段代碼將把[unowned self]添加到閉包的捕獲列表中。這意味著,self被捕獲為無主引用,而不是強引用。

這種技術徹底解決了引用循環問題!

這里使用的語法實際上是一個較長的捕捉語法的簡寫,這里引入了一個新的標識符。請考慮下面更長的形式:

  1. var closure = { 
  2.  
  3.   [unowned newID = self] in 
  4.  
  5.   // Use unowned newID here... 
  6.  

在這里,newID是self的一個unowned副本。在閉包范圍外部,self保留其原有的意義。如你上面使用的簡短形式,創建了一個新的self變量——此變量只是在閉包范圍內“遮擋”住現有的self變量。

在你編寫代碼中,self和閉包completePhoneNumber之間的關系應當是無主(unowned)引用。如果您確信閉包中的一個引用對象將永遠不會釋放,那么你可以使用unowned引用。如果這個對象確定要釋放內存,那么就存在麻煩了。

請把下面的代碼添加到上面示例工程文件的結尾:

  1. class WWDCGreeting { 
  2.  
  3.   let who: String  
  4.  
  5.   init(who: String) { 
  6.  
  7.     self.who = who 
  8.  
  9.   }  
  10.  
  11.   lazy var greetingMaker: () -> String = { 
  12.  
  13.     [unowned self] in 
  14.  
  15.     return "Hello \(self.who)." 
  16.  
  17.   } 
  18.  
  19.  
  20. let greetingMaker: () -> String  
  21.  
  22. do { 
  23.  
  24.   let mermaid = WWDCGreeting(who: "caffinated mermaid") 
  25.  
  26.   greetingMaker = mermaid.greetingMaker 
  27.  
  28. }  
  29.  
  30. greetingMaker() // TRAP! 

程序運行時將引發一個運行時異常,因為閉包期望self.who仍然有效,但是當mermaid變量脫離其范圍時會被釋放。這個例子似乎有些做作,但在現實開發中很容易發生這種情況——例如,當您使用閉包要很晚時候才運行某些東西的時候(譬如在異步網絡調用完成后)。

好,下面請把WWDCGreeting中的greetingMaker變量更改成如下這樣:

  1. lazy var greetingMaker: () -> String = { 
  2.  
  3.   [weak self] in 
  4.  
  5.   return "Hello \(self?.who)." 
  6.  

這段代碼中,你對原來的greetingMaker作出兩處修改。首先,使用weak替換unowned。其次,由于self成為weak類型,所以你需要使用self?.who來訪問who屬性。

再次運行示例工程時系統不再崩潰了,但你在側邊欄中得到一個奇怪的輸出結果:“Hello, nil.”。也許,這是可以接受的,但更多的情況下當對象已經一去不復返時你往往想做一些完全與此不同的事情。Swift的guard let語句使得實現這一目的非常容易。

讓我們最后一次重新修改閉包吧,使其看起來像下面這樣:

  1. lazy var greetingMaker: () -> String = { 
  2.  
  3.   [weak self] in 
  4.  
  5.   guard let strongSelf = self else { 
  6.  
  7.     return "No greeting available." 
  8.  
  9.   } 
  10.  
  11.   return "Hello \(strongSelf.who)." 
  12.  

guard語句綁定一個來自于weak welf的新變量strongSelf。如果self是nil,閉包將返回“No greeting available.”另一方面,如果self不是nil,strongSelf將進行強引用;這樣一來,對象將被確保一直有效,直到閉包末端處。

上述這一術語,有時也被稱為強弱舞蹈(strong-weak dance),它是Swift語言中處理閉包中這種行為的一種強有力的模式。

一個引用循環的完整例子

現在,你已經明白了Swift語言中的ARC原則了,你也理解了什么是引用循環,以及如何打破它們。接下來,讓我們來看看一個真實世界的例子。

首先,請下載我提供的一個啟動項目(https://koenig-media.raywenderlich.com/uploads/2016/08/ContactsStarterProject-1.zip),并在Xcode 8(或更新版本)中打開,因為Xcode 8添加了你要使用的一些有趣的新功能。

之后,構建并運行這個項目,你會看到顯示以下內容: 

這是一個簡單的聯系人應用程序。你可以隨意點擊一個聯系人以獲取更多信息,或者使用右上角的【+】按鈕添加一個聯系人。

現在,我們來概述一下關鍵代碼的作用:

  • ContactsTableViewController:顯示數據庫所有聯系人對象。
  • DetailViewController:顯示每一個具體聯系人的詳細信息。
  • NewContactViewController<:允許用戶添加一個聯系人。
  • ContactTableViewCell:一個用于顯示聯系人詳細信息的表格單元格。
  • Contact:對應于數據庫中的聯系人。
  • Number:用于存儲電話號碼。

然而,這個工程中存在一些可怕的錯誤:代碼中存在引用循環!在相當一段時間內,您的用戶不會注意到這一點,因為這個問題中存在的泄漏對象很小——它們的尺寸使得它更難追查。幸運的是,Xcode 8中提供了一個新的內置工具來幫助你找到哪怕是最小的泄漏。

生成并再次運行應用程序。嘗試著刪除三或四個聯系人。看起來,他們已經完全消失了,對吧? 

當應用程序仍在運行時,移動到Xcode的底部,然后單擊【Debug Memory Graph】按鈕: 

請觀察圖中Xcode 8引入的新的問題類型:Runtime Issues。它們看起來像是在一個紫色方框中放上了一個白色的感嘆號一樣的圖標,請參考顯示在下面這個截圖中選擇的部分: 

在導航器中,選擇某一個有問題的聯系人對象。則循環引用現在清晰可見:Contact和Number對象保持彼此存活——通過彼此相互引用。請參考下圖: 

這種類型圖表提供了你尋找代碼中錯誤的一種形象標志。請考慮一下:一個聯系人在沒有號碼情況下能夠正常存在,但一個號碼在沒有聯系人時是不應當存在的。那么,你將如何解決這個循環問題呢?

強烈建議讀者先自己嘗試解決一下這個問題。然后,再對照下面的解決方案。

其實,有兩種可能的解決辦法:你可以使從Contact到Number的關系成為弱引用類型,也可以使從Number到Contact的關系成為unowned類型。這兩種方案都能夠有效地解決循環引用問題。

【注意】蘋果官方文檔(https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html)中推薦一個父對象應當強引用一個子對象。這意味著,應當在Contact中強引用Number,而使Number無主引用Contact。請參考下面的代碼答案:

  1. class Number { 
  2.  
  3.   unowned var contact: Contact 
  4.  
  5.   // Other code... 
  6.  
  7.  
  8. class Contact { 
  9.  
  10.   var number: Number? 
  11.  
  12.   // Other code... 
  13.  

循環引用與值類型和引用類型

Swift類型分為引用類型(如類)和值類型(如結構或枚舉)。主要的區別是,值類型在傳來傳去時被復制,而引用類型共享引用信息的一個副本。

這是否意味著,使用值類型時就不存在循環問題?是的:如果一切都使用值類型復制的話,就不會存在循環引用關系,因為不會創建真正的引用。你至少需要兩個引用才構成一個循環,是吧?

不妨回到剛才的工程代碼中,在結尾處加上以下內容:

  1. struct Node { // Error 
  2.  
  3.   var payload = 0 
  4.  
  5.   var next: Node? = nil 
  6.  

運行一下,你會注意到編譯器無法正常通過編譯。原因在于,一個結構(值類型)不能是遞歸的或使用它自己的一個實例;否則,這種類型的結構將有無限的大小。現在,我們將其更改為像下面這樣的一個類:

  1. class Node { 
  2.  
  3.   var payload = 0 
  4.  
  5.   var next: Node? = nil 
  6.  

自我引用對于類(即引用類型)來說不是問題,所以編譯器錯誤消失了。

現在,再添加下列代碼到您的上述文件中:

  1. class Person { 
  2.  
  3.   var name: String 
  4.  
  5.   var friends: [Person] = [] 
  6.  
  7.   init(name: String) { 
  8.  
  9.     self.name = name 
  10.  
  11.     print("New person instance: \(name)") 
  12.  
  13.   }  
  14.  
  15.   deinit { 
  16.  
  17.     print("Person instance \(name) is being deallocated") 
  18.  
  19.   } 
  20.  
  21. }  
  22.  
  23. do { 
  24.  
  25.   let ernie = Person(name: "Ernie") 
  26.  
  27.   let bert = Person(name: "Bert")  
  28.  
  29.   ernie.friends.append(bert) // Not deallocated 
  30.  
  31.   bert.friends.append(ernie) // Not deallocated 
  32.  

這里的例子提供了一個值類型和引用類型混合形成引用循環的例子。

ernie和bert正常存活——通過在他們的friends數組中保持互相引用,雖然數組本身是一個值類型。如果把這個數組改成unowned類型,則Xcode中會顯示一個錯誤:unowned只適用于類類型。

為了打破這里的循環,你必須創建一個泛型包裝對象,并用它來添加實例到數組中。如果你不知道什么是泛型或如何使用它們,請查看官方網站中有關泛型的教程。

好,現在請在上面Person類的定義上面添加如下代碼:

  1. class Unowned<T: AnyObject> { 
  2.  
  3.   unowned var value: T 
  4.  
  5.   init (_ value: T) { 
  6.  
  7.     self.value = value 
  8.  
  9.   } 
  10.  

然后,更改Person中friends屬性的定義為如下樣子:

  1. var friends: [Unowned<Person>] = [] 

最后,把do語句塊修改成看起來像下面這樣:

  1. do { 
  2.  
  3.   let ernie = Person(name: "Ernie") 
  4.  
  5.   let bert = Person(name: "Bert") 
  6.  
  7.   ernie.friends.append(Unowned(bert)) 
  8.  
  9.   bert.friends.append(Unowned(ernie)) 
  10.  

現在,ernie和bert都能夠正常釋放了!

在此,friends數組不再是Person對象的一個集合了,而是成為無主對象的集合——此對象用作Person實例的包裝器。

為了從Unowned對象中訪問Person對象,我們可以使用value屬性,像這樣:

  1. let firstFriend = bert.friends.first?.value // get ernie 

小結

完整的示例工程下載地址是https://koenig-media.raywenderlich.com/uploads/2016/08/MemoryManagement.playground.zip

通過本文學習,你對Swift的內存管理應當有了一個很好的了解,并知道ARC是如何工作的。

如果你想更深入地了解Swift是如何實現弱引用的,請參考一下邁克的博客文章“Swift弱引用”(https://www.mikeash.com/pyblog/friday-qa-2015-12-11-swift-weak-references.html)。

 

【51CTO譯稿,合作站點轉載請注明原文譯者和出處為51CTO.com】

責任編輯:陳琳 來源: 51CTO
相關推薦

2016-10-12 10:28:55

Swift開發周刊

2010-09-27 13:26:31

JVM內存管理機制

2010-07-23 09:34:48

Python

2013-09-29 15:11:46

Linux運維內存管理

2010-09-26 13:23:13

JVM內存管理機制

2022-06-01 16:01:58

MySQL內存管理系統

2011-06-29 17:20:20

Qt 內存 QOBJECT

2010-12-10 15:40:58

JVM內存管理

2020-11-08 14:32:01

JavaScript變量內存管理

2022-02-28 10:25:17

Python參數傳遞拷貝

2019-01-23 17:08:52

Python內存管理RealPython

2020-08-18 19:15:44

Redis內存管理

2009-10-22 17:39:34

CLR內存管理

2011-08-18 13:28:35

Objective-C內存

2009-09-02 09:23:26

.NET內存管理機制

2017-08-17 17:09:28

PostgreSQL 數據塊機制

2010-01-06 10:23:47

.NET Framew

2009-07-08 15:10:00

Servlet會話管理

2021-02-07 09:02:28

內存管理length

2009-09-23 17:48:00

Hibernate事務
點贊
收藏

51CTO技術棧公眾號

欧美又粗又大又爽| 国产在线日韩欧美| 亚洲系列中文字幕| 狠狠干狠狠操视频| 人人澡人人添人人爽一区二区| 国产精品自在欧美一区| 亚洲每日更新| 91麻豆精品国产91久久久久| 99久久国产综合精品五月天喷水| 青春有你2免费观看完整版在线播放高清| 视频一区免费在线观看| 久久综合免费视频影院| 一二三不卡视频| 欧美综合影院| 五月激情综合婷婷| 日本黄色播放器| 香蕉视频911| 另类小说视频一区二区| 国内精品中文字幕| 91麻豆天美传媒在线| 免费黄色在线网址| 99re6热只有精品免费观看| 色综合中文字幕| 日韩在线视频在线| 午夜在线观看视频| 99久久精品国产网站| 91免费人成网站在线观看18| 69视频免费在线观看| 欧美gayvideo| 亚洲女人被黑人巨大进入| 韩国一区二区在线播放| 精品亚洲美女网站| 亚洲成人手机在线| 欧美另类极品videosbest最新版本| 五月天丁香综合久久国产| 97精品人妻一区二区三区在线| 午夜在线视频一区二区区别| 久久久久久久久久久免费| 欧美h片在线观看| 国产亚洲一区二区三区不卡| 亚洲精品久久久久久久久久久久久 | 亚洲不卡免费视频| 久久精品国产网站| 精品久久久在线观看| 色妞久久福利网| 中文字幕一区二区三区人妻不卡| 亚洲一区二区三区免费| 91精品国产免费| 日本77777| 综合欧美精品| 91精品国产综合久久久久| 亚洲美女爱爱视频| 婷婷激情成人| 欧美精品日韩一本| 在线视频观看91| 日韩成人精品一区二区三区| 欧美三级一区二区| 中国黄色片免费看| 影音先锋人妻啪啪av资源网站| 高清不卡av| 色综合久久66| 日本新janpanese乱熟| 欧美片第1页| 在线观看av一区| 香蕉视频网站入口| 国产精品传媒麻豆hd| 狠狠v欧美v日韩v亚洲ⅴ| 国产精品第七十二页| 亚洲欧美综合另类| 肉色丝袜一区二区| 国产美女被下药99| 国产免费叼嘿网站免费| 国产精品影视在线观看| 国产成人精品在线看| 国产精品女人久久久久久| 亚洲综合五月天婷婷丁香| 国内精品伊人久久久久av影院| 亚洲aa中文字幕| 欧美一区二区在线观看视频| 99久久er热在这里只有精品66| 欧美精品一区二区三区四区五区 | 日韩中文字幕在线不卡| 国内在线视频| 精品国产乱码久久久久久虫虫漫画 | 亚洲一区欧美在线| 午夜在线一区| 国产日韩中文在线| 成人免费一级视频| 久久久久9999亚洲精品| 亚洲一卡二卡三卡四卡无卡网站在线看| 黄色网页在线看| 一区二区三区视频在线观看| 国产伦精品一区二区三区四区视频_| 刘亦菲一区二区三区免费看| 欧美日韩精品欧美日韩精品| 俄罗斯女人裸体性做爰| 蜜桃成人av| 国产日韩视频| 久久久久久网站| 日韩一级在线视频 | 麻豆传媒在线看| 色婷婷综合久久久久久| 日韩最新在线视频| 日韩精品――中文字幕| 美国欧美日韩国产在线播放| 国产乱人伦精品一区二区| 国产三级视频在线看| 依依成人综合视频| 男操女免费网站| 国产精品一区二区三区美女| 色一情一乱一区二区| 妓院一钑片免看黄大片| 一级黄色片在线| 国产一区二区三区综合| 免费99视频| av网站网址在线观看| 一本到三区不卡视频| 丰满少妇中文字幕| 日本一本不卡| 欧美在线一级va免费观看| www.蜜桃av.com| 国产精品毛片大码女人| 国产亚洲欧美在线视频| 亚洲不卡视频| www.日韩视频| 日韩美一区二区| 99久久精品一区| 久久99精品久久久久久青青91| 久久久久久久久久毛片| av在线不卡顿| 欧美在线视频一区| 日韩在线观看视频一区| 亚洲精品免费电影| 婷婷激情小说网| 日韩在线观看| 国产精品电影网站| 青青草视频免费在线观看| 亚洲国产中文字幕在线视频综合| 国内国产精品天干天干| 日韩理论电影大全| 国产成人精品电影| 男同在线观看| 色婷婷综合久久| 亚洲午夜久久久久久久久红桃 | 国产日本精品| 豆国产97在线| 欧洲一区二区三区| 精品少妇一区二区三区免费观看 | 国产精品久久九九| 日本不卡影院| 日韩免费观看高清完整版 | 亚洲综合婷婷| 亚洲一区二区三区sesese| 暖暖日本在线观看| 在线观看一区| 国产一区二区精品丝袜| 无码日韩精品一区二区| 国产午夜亚洲精品不卡| 青青草精品视频在线观看| 精品一区av| 国产精品亚洲美女av网站| 福利成人在线观看| 欧美日韩一级二级| 懂色av粉嫩av蜜臀av一区二区三区| 奇米一区二区三区av| 亚洲激情电影在线| 色999久久久精品人人澡69| 久久久成人精品视频| www.四虎在线观看| 五月天中文字幕一区二区| 国产chinese中国hdxxxx| 色噜噜在线播放| 国产精品对白交换视频 | 日韩中文字幕在线不卡| 风间由美一区二区av101| 2018国产精品视频| 成人亚洲综合天堂| 欧美高清性hdvideosex| 麻豆亚洲av熟女国产一区二| av影院午夜一区| 欧美成人精品欧美一级乱| 日韩在线不卡| 国产精品一区在线播放| 久久uomeier| 不卡毛片在线看| 天天在线女人的天堂视频| 国产aⅴ综合色| 777精品视频| 深夜福利在线视频| 欧美日韩小视频| 精品午夜福利视频| 国产视频一区不卡| 女教师高潮黄又色视频| 视频在线在亚洲| 中国 免费 av| 天堂av一区二区三区在线播放| 国产精品亚洲片夜色在线| 俄罗斯一级**毛片在线播放| 亚洲欧美在线免费观看| 成人短视频在线观看免费| 亚洲免费不卡视频| 在线观看国产精品网站| www.av视频| 欧美国产一区视频在线观看| 人妻激情偷乱视频一区二区三区| 久久精品九九| 欧美一区二区三区综合| 国产亚洲第一伦理第一区| 99re在线观看视频| 欧美成人福利| 日本高清久久天堂| 男插女视频久久久| 色婷婷av一区二区三区在线观看| 色婷婷中文字幕| 欧美一级理论性理论a| www.五月婷婷.com| 亚洲成av人片www| 国产免费一区二区三区四区| 久久久噜噜噜久噜久久综合| 麻豆tv在线观看| 久久99精品久久久久久国产越南| 成熟了的熟妇毛茸茸| 欧美一区免费| 一区二区三区四区视频精品免费 | 久久综合狠狠综合久久综合88 | 91亚洲无吗| 国产精品视频yy9099| 小早川怜子影音先锋在线观看| 欧美男插女视频| 秋霞a级毛片在线看| 国产亚洲精品一区二555| 婷婷伊人综合中文字幕| 精品久久国产老人久久综合| 国产普通话bbwbbwbbw| 亚洲一区电影| 成人精品一区二区三区电影黑人| 伊人久久精品一区二区三区| 久久久日本电影| 亚洲国产精品精华素| 久久天天躁夜夜躁狠狠躁2022| 国产福利在线视频| 亚洲一级黄色片| 国产主播福利在线| 国产亚洲精品美女久久久| 欧美91精品久久久久国产性生爱| 亚洲国产成人久久| 黑人精品一区二区三区| 精品免费99久久| 亚洲黄色在线播放| 精品久久人人做人人爽| 亚洲精品第五页| 精品久久久久99| 十八禁一区二区三区| 婷婷精品在线观看| 成人淫片在线看| 精品国产亚洲一区二区三区大结局| 成人黄色片在线| 国产精品麻豆| 91久久精品一区二区别| 亚洲综合色婷婷在线观看| 99久久精品无码一区二区毛片| 伊人www22综合色| 国产精品二区在线| 六月丁香久久丫| 欧美日韩精品免费观看视一区二区| 欧美禁忌电影网| 伊人狠狠色丁香综合尤物| 一区二区免费不卡在线| 毛片av在线播放| 日韩一级免费| 黄色一级二级三级| 精品一区二区久久久| 中文字幕一二三| 9l国产精品久久久久麻豆| 男男做爰猛烈叫床爽爽小说| 国产日韩一级二级三级| 国产三级aaa| 夜夜精品浪潮av一区二区三区| 日韩乱码一区二区| 在线一区二区视频| 国产一区二区三区在线观看 | 日本不卡不码高清免费观看| 九九视频精品在线观看| 国产一区欧美二区| 国产人妻黑人一区二区三区| 久久精品亚洲精品国产欧美kt∨ | 国产最新视频在线| 久久久精品美女| 免费成人在线电影| 国产女同一区二区| 第四色在线一区二区| 日韩电影在线播放| 国产一区久久| 亚洲中文字幕久久精品无码喷水| 久久99深爱久久99精品| 手机在线成人av| 国产精品九色蝌蚪自拍| 国产视频91在线| 在线播放中文字幕一区| 成人在线观看黄| 日韩精品福利网| 国产xxx在线观看| 日本一区二区免费在线观看视频| 欧美日韩中文字幕在线观看| 色8久久人人97超碰香蕉987| 99久久久无码国产精品免费| 亚洲午夜国产成人av电影男同| 羞羞视频在线免费国产| 国产精品精品久久久久久| gogo人体一区| 精品一区二区成人免费视频| 性感少妇一区| 国产性猛交96| 亚洲欧洲av在线| 欧美超碰在线观看| 欧美精品一区二区在线播放| 欧美极品另类| 日韩av电影在线网| 精品精品国产毛片在线看| 99热这里只有精品7| 日韩av在线发布| 中文字幕亚洲在线| 香蕉视频网站在线观看| 欧美专区第一页| 国内露脸中年夫妇交换精品| 女女同性女同一区二区三区按摩| 日韩电影在线一区二区| 精品夜夜澡人妻无码av| 亚洲午夜在线视频| jlzzjlzz亚洲女人18| 精品国产一区二区三区久久久狼| 台湾佬成人网| 久久精品中文字幕一区二区三区| 狠狠干成人综合网| 国产sm在线观看| 亚洲美女偷拍久久| 国产精品一二三四五区| 日韩在线免费高清视频| 成人午夜一级| 五月天亚洲综合情| 日本欧美大码aⅴ在线播放| 加勒比一区二区| 色久综合一二码| 欧美人与性囗牲恔配| 日韩中文字幕区一区有砖一区 | 国产精品日本一区二区三区在线| 日韩国产一区久久| 日韩国产欧美在线播放| 成人免费网站黄| 91福利在线免费观看| 国产精品二线| 国产色婷婷国产综合在线理论片a| 精品大片一区二区| 黄色aaa级片| 国产精品福利在线播放| 国产伦子伦对白视频| 久久视频这里只有精品| 日韩综合一区二区三区| 人人妻人人澡人人爽欧美一区 | 欧美午夜不卡在线观看免费| 国产一级二级三级在线观看| 国产精品成人一区二区三区吃奶| 成人看的视频| 国产精品永久在线| 深夜av在线| 久久精品日产第一区二区三区精品版 | 在线观看国产成人av片| 成人国产在线| 国产一区二区三区播放| www..com久久爱| 波多野结衣人妻| 美女精品久久久| 哺乳一区二区三区中文视频 | 亚洲精品美女视频| 欧美大胆性生话| 一区国产精品| 成人小视频在线| 永久免费无码av网站在线观看| 一区二区三区国产在线观看| 成人影院网站ww555久久精品| 97视频在线看| 国产精品一区二区三区视频网站| 99re资源| 视频在线在亚洲| 欧美精品成人久久| 亚洲精品之草原avav久久| 97人人做人人爽香蕉精品| 黄色录像特级片| 26uuu久久天堂性欧美| 一级黄色片免费| 国外成人在线播放| 日韩精品不卡一区二区| 日本人添下边视频免费| 欧美在线免费视屏| 牛牛精品视频在线| 日韩影片在线播放| 成人综合在线观看| 中文字幕乱伦视频| 久久久久久中文| 日韩成人精品一区二区|