Swift內存管理:編程
Swift 和 OC 用的都是ARC的內存管理機制,它們經過 ARC 能夠很好的管理對象的回收,大部分的時候,程序猿無需關心 Swift 對象的回收。swift
注意:閉包
只有引用類型變量所引用的對象才須要使用引用計數器進行管理,對於枚舉、結構體等,他們都是值類型的。所以不須要使用引用計數進行管理。app
一:理解ARCide
1: ARC 自動統計改對象被多少引用變量引用,這個值就是咱們常說的引用計數器。函數
2: 每當引用計數器計數變爲0的時候,ARC就會回收這個對象。spa
好比,如今咱們作一個針對大學生用戶的APP,咱們寫了一個User類,這個類裏面有姓名、年紀、班級三個屬性,看整個文件代碼: 對象
import UIKit class ComNavigationController: UINavigationController { class User { var name:String var age:Int var classes:String init(name:String,age:Int,classes:String) { self.name = name self.age = age self.classes = classes } deinit{ print("\(self.name) 用戶即將被銷燬") } } override func viewDidLoad() { var user1:User? user1 = User(name:"zhangxu",age:24,classes:"三年二班") /// 建立了一個User對象 用戶1這個變量是指向User對象的,這時候User對象的引用計數爲1 var user2:User? user2 = user1 var user3:User? user3 = user1 // 這時候被變量2 和變量3 都引用了,User對象的引用計數就變成了------ 3 print(user2) print(user3) user1 = nil user2 = nil user3 = nil // 1 2 3 都置爲你了 用戶都再也不引用User對象 // 這時候 User對象不被任何變量引用,引用計數器就變成了0 // 引用計數器編程了 0 ,ARC就會回收該對象 super.viewDidLoad() // Do any additional setup after loading the view. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } /* // MARK: - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { // Get the new view controller using segue.destinationViewController. // Pass the selected object to the new view controller. } */ }
二:強引用循環blog
大部分時候,ARC可以很好的處理程序中對象的內存回收,但若是這兩個對象之間存在着相互的引用,也就是當兩個對象都使用存儲屬性相互的引用對方的時候,此時兩個對象的引用計數都等於 1 ,但實際上它們都沒有被真正的引用變量所引用,就像上面的 user1 這樣的變量。這時候的 ARC是沒法回收它們的。內存
看下面的代碼示例:
class teacher { var name:String var age:Int var student1:student? init(name:String,age:Int) { self.name = name self.age = age } deinit{ print("老師對象被回收"); } } class student { var name:String var age:Int var teacher1:teacher? init(name:String,age:Int) { self.name = name self.age = age } deinit{ print("老師對象被回收"); } } var stu:student? = student(name: "zhangxiaxu",age: 24) var tea:teacher? = teacher(name: "wangnima",age: 200) // 就在這裏相互引用,造成了強引用循環 stu?.teacher1 = tea tea?.student1 = stu stu = nil tea = nil
解釋一下:
上面的代碼執行完以後,兩個對象之間再也不有真正的引用變量引用他們,但兩個對象之間的相互引用,造成了"強引用循環",此時它們的引用計數爲 1 ,ARC也不會去回收它們,任何一個對象釋放,都要等對方先釋放,所以兩個對象只愛你誰都沒辦法被回收,這兩個對象在這時候就變成了垃圾。爲告終局上面的強引用循環,咱們就必須讓一方先放手,容許對方先釋放。Swift這時候提供了兩種機制: 弱引用和無主引用
三:使用弱引用解決強引用循環
弱引用不會增長對方的引用計數,所以不會阻止ARC回收被引用的實例,這樣就避免了造成強引用循環, 在定義屬性的 var 關鍵字以前加 weak 就定義了弱引用。
注意點:
1 : 弱引用變量要求該變量必需要能被設置成 nil ,也就是弱引用的屬性最好是使用可選類型來定義。
2 : 弱引用的屬性只能聲明爲變量類型,由於該屬性在運行期內只有可能會發生變化,所以不能設置成常量。
3 :也不必把兩個相互引用的屬性都設置成弱引用,有一個就能夠了。
因此,要是使用弱引用解決上面的強引用循環的,只需按下面聲明屬性:
// 修改teacher類的 student 爲弱引用屬性 weak var student1:student? // 或者修改 student 類的 teacher 爲弱引用屬性 weak var teacher1:teacher?
四:使用無主引用解決強引用循環
與弱引用類似的是,無主引用也不會增長對方的引用計數,無主引用於弱引用的區別:
無主引用不容許接受nil,意思就是這個屬性要一直有值!所以無主引用只能定義爲非可選類型。
在定義屬性 var 或者 let 以前,添加 unowned 關鍵字便可。上面的強引用要用無主引用解決的話,看下面代碼:
// 聲明 teacher 類的 student 屬性爲無主引用 且 不能是可選類型。 unowned let student1:student // 或者聲明 student 類的 teacher 屬性爲無主引用 unowned let teacher1:teacher
五:閉包的強引用循環解決
上面給出了兩種方式,說說他們的使用場景的一個區別。
當閉包和捕獲的對象老是相互引用,而且老是同事銷燬時,應該將閉包內捕獲的實例定義爲無主引用。
當閉包捕獲的引用變量有多是 nil 時,將閉包捕獲的引用變量定義爲弱引用。
若是程序將該對象自己傳入了閉包,那麼閉包自己就會捕獲該對象,因而該對象就持有了閉包屬性,反過來,閉包也持有對象,這樣子就造成了強引用。
import UIKit class ComNavigationController: UINavigationController { class teacher { var name:String var age:Int lazy var findteacher:() ->String = { [unowned self] in return "該老師名字是\(self.name) 年紀是\(self.age)" //[weak self] in //return "該老師名字是\(self!.name) 年紀是\(self!.age)" } init(name:String,age:Int) { self.name = name self.age = age } deinit{ print("老師對象被回收"); } } override func viewDidLoad() { super.viewDidLoad() var tea:teacher? = teacher(name: "葫蘆娃",age: 24) var find:(() ->String)? = tea!.findteacher tea = nil find = nil // Do any additional setup after loading the view. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }
解釋一下:
上面代碼中,咱們看在 viewdidload 方法中,先建立了一個 teacher 對象,而且賦值給 tea 變量,接下來有定義了一個函數類型的變量,而且將 teacher 實例的 findteacher 屬性賦值給該變量,到後面tea 和 find 變量都賦值爲 nil , 此時沒有引用變量引用 teacher 對象和閉包對象,但兩個對象之間的相互引用就造成了強引用循環。
固然,咱們只是說造成了,上面的代碼裏面也已經給出瞭解決的方法,尤爲注意一點,就是使用無主引用和弱引用時候 self 的區別。