Swift 內存管理詳解

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 的區別。

相關文章
相關標籤/搜索