swift-自动引用计数器

/*
     • 自动引用计数的工作机制
     • 自动引用计数实践
     • 类实例之间的循环强引用
     • 解决实例之间的循环强引用
     • 闭包引起的循环强引用
     • 解决闭包引起的循环强引用
     
     */
    
    //自动引用计数器的工作机制
    /*当你每次创建一个类的新的实例的时候,ARC 会分配一块内存来储存该实例信息。内存中会包含实例的类型信 息,以及这个实例所有相关的存储型属性的值。
    此外,当实例不再被使用时,ARC 释放实例所占用的内存,并让释放的内存能挪作他用。这确保了不再被使用的 实例,不会一直占用内存空间。
    然而,当 ARC 收回和释放了正在被使用中的实例,该实例的属性和方法将不能再被访问和调用。实际上,如果你 试图访问这个实例,你的应用程序很可能会崩溃。
    为了确保使用中的实例不会被销毁,ARC 会跟踪和计算每一个实例正在被多少属性,常量和变量所引用。哪怕实 例的引用数为1,ARC都不会销毁这个实例。
    为了使上述成为可能,无论你将实例赋值给属性、常量或变量,它们都会创建此实例的强引用。之所以称之
    为“强”引用,是因为它会将实例牢牢地保持住,只要强引用还在,实例是不允许被销毁的。*/
    
    //自动引用计数实践
    class Person {
    
        let name: String
        init(name: String) {
            self.name = name
            print("\(name) is being initialized")
        }
        deinit {
            print("\(name) is being deinitialized")
        }
        
    }
    
   // Person 类有一个构造函数,此构造函数为实例的 name 属性赋值,并打印一条消息以表明初始化过程生效。 on 类也拥有一个析构函数,这个析构函数会在实例被销毁时打印一条消息。
    var reference1: Person?
    var reference2: Person?
    var reference3: Person?
    
    reference1 = Person(name: "John Appleseed")
   // 应当注意到当你调用 Person 类的构造函数的时候, “John Appleseed is being initialized” 会被打印出来。由 此可以确定构造函数被执行。
    reference2 = reference1
    reference3 = reference1
    
    //如果你通过给其中两个变量赋值 nil 的方式断开两个强引用(包括最先的那个强引用),只留下一个强引用, rson 实例不会被销毁:
    reference1 = nil
    reference2 = nil
    //在你清楚地表明不再使用这个 Person 实例时,即第三个也就是最后一个强引用被断开时,ARC 会销毁它:
    
    reference3 = nil
    // 打印 “John Appleseed is being deinitialized”
    
    
    //类实例之间的循环引用
    //在上面的例子中,ARC 会跟踪你所新创建的 Person 实例的引用数量,并且会在 Person 实例不再被需要时销毁它。
    class Person1 {
    
        let name: String
        init(name: String) {
            self.name = name
        }
    var apartment: Apartment?
        deinit {
            print("\(name) is being deinitialized")
        }
    }
    
    class Apartment {
    
        let unit: String
        init(unit: String) {
            self.unit = unit
        }
       weak var tenant:Person1?
        deinit {
             print("Apartment \(unit) is being deinitialized")
        }
        
    }
    
    var john: Person?
    var unit4A: Apartment?
    
    john = Person(name: "John Appleseed")
    unit4A = Apartment(unit: "4A")
    
   // 不幸的是,这两个实例关联后会产生一个循环强引用。 Person 实例现在有了一个指向 Apartment 实例的强引 用,而 Apartment 实例也有了一个指向 Person 实例的强引用。因此,当你断开 john 和 unit4A 变量所持有的强 引用时,引用计数并不会降为 0 ,实例也不会被 ARC 销毁:
    john = nil
    unit4A = nil
    //注意,当你把这两个变量设为 nil 时,没有任何一个析构函数被调用。循环强引用会一直阻止 Person 和 Apartme nt 类实例的销毁,这就在你的应用程序中造成了内存泄漏。
    
    
    //解决实例之间的循环引用
    //弱引用:声明属性或者变量时,在前面加上 weak 关键字表明这是一个弱引用。
    
   // 然后跟之前一样,建立两个变量( john 和 unit4A )之间的强引用,并关联两个实例:
    
    
    //无主引用
    //和弱引用类似,无主引用不会牢牢保持住引用的实例。和弱引用不同的是,无主引用在其他实例有相同或者更长 的生命周期时使用。你可以在声明属性或者变量时,在前面加上关键字 unowned 表示这是一个无主引用。
    //重要
   // 使用无主引用,你必须确保引用始终指向一个未销毁的实例。
   // 如果你试图在实例被销毁后,访问该实例的无主引用,会触发运行时错误。
    
    class Customer {
    
        let name:String
        var card:CreditCard?
        init(name: String) {
            self.name = name
        }
        deinit {
            print("\(name) is being deinitialized")
        }
    }
    
        class CreditCard {
            let number:NSInteger
            unowned let customer: Customer
            init(number:NSInteger , customer: Customer) {
                self.number = number
                self.customer = customer
            }
            deinit {
                print("Card #\(number) is being deinitialized")
            
            }
        }
    
    var john1: Customer?
    john1 = Customer(name: "John Appleseed")
    john1!.card = CreditCard(number: 1234_5678_9012_3456, customer: john1!)

    
    //无主引用以及隐式解析可选属性
    //两个属性都必须有值,并且初始化完成后永远不会为 nil 。在这种场 景中,需要一个类使用无主属性,而另外一个类使用隐式解析可选属性。

    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 的实例,而不产生循环强引用,并且 的属性能被直接访问,而不需要通过感叹号来展开它的可选值:
    var country = Country(name: "Canada", capitalName: "Ottawa")
    print("\(country.name)'s capital city is called \(country.capitalCity.name)")
    
    
    
    //闭包引起的循环引用
    //循环强引用还会发生在当你将一个闭包赋值给类实例的某个属性,并且这个闭包体中又使用了这个类实例时。这个闭包体中可能访问了实例的某个属性,例如 self.someProperty ,或者闭包中调用了实例的某个方法,例如 self.someMethod() 。这两种情况都导致了闭包“捕获” self ,从而产生了循环强引用。
    
    //循环强引用的产生,是因为闭包和类相似,都是引用类型。
    
    //下面的例子为你展示了当一个闭包引用了 self 后是如何产生一个循环强引用的。例子中定义了一个叫 t 的类,用一种简单的模型表示 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")
//            }
//            
//        }
    
    //解决闭包引起的循环引用
    //只要在闭包内使用 self 的成员,就要用 self.someProperty 或者 self.someMethod() (而 不只是 someProperty 或 someMethod() )。这提醒你可能会一不小心就捕获了 self 。

    //定义捕获列表 
   // 捕获列表中的每一项都由一对元素组成,一个元素是 weak 或 unowned 关键字,另一个元素是类实例的引用(例如 self )或初始化过的变量(如 delegate = self.delegate! )。这些项在方括号中用逗号分开。
    //如果闭包有参数列表和返回类型,把捕获列表放在它们前面:
    
    //lazy var someClosure: (NSInteger ,String) -> String = {
    
      //  [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in // 这里是闭包的函数体
   // }
    
    //如果闭包没有指明参数列表或者返回类型,即它们会通过上下文推断,那么可以把捕获列表和关键字 in 放在闭包 最开始的地方:
    //lazy var someClosure: Void -> String = {
     //   [unowned self, weak delegate = self.delegate!] in // 这里是闭包的函数体
    //}
    
    //弱引用和无主引用

    //在闭包和捕获的实例总是互相引用并且总是同时销毁时,将闭包内的捕获定义为 无主引用 。
    //相反的,在被捕获的引用可能会变为 nil 时,将闭包内的捕获定义为 弱引用 。弱引用总是可选类型,并且当引用 的实例被销毁后,弱引用的值会自动置为 nil 。这使我们可以在闭包体内检查它们是否存在。

    
    //前面的 HTMLElement 例子中,无主引/弱引用用是正确的解决循环强引用的方法。这样编写 HTMLElement 类来避免循环强 引用:
    class HTMLElement {
        let name: String
        let text: String?
        lazy var asHTML: (Void) -> String = {
           [unowned self] in//[weak 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")
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,753评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,668评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,090评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,010评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,054评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,806评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,484评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,380评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,873评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,021评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,158评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,838评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,499评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,044评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,159评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,449评论 3 374
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,136评论 2 356

推荐阅读更多精彩内容