精华内容
下载资源
问答
  • swift 弱引用It’s a good practice to know how memory management works in Swift. Swift uses Automatic Reference Counting (ARC) to manage the app’s memory automatically without writing any additional ...

    swift 弱引用

    It’s a good practice to know how memory management works in Swift. Swift uses Automatic Reference Counting (ARC) to manage the app’s memory automatically without writing any additional code. It deallocates the memory of instances when it is no longer used. But sometimes we have to provide more information to avoid memory leaks and deadlocks.

    了解内存管理如何在Swift中工作是一个好习惯。 Swift使用自动引用计数 (ARC)来自动管理应用程序的内存,而无需编写任何其他代码。 当不再使用实例时,它将分配实例的内存。 但是有时我们必须提供更多信息,以避免内存泄漏死锁

    ARC only applies on instance of classes because classes are reference type. It didn’t applies on structures and enumerations because they are value type.

    ARC仅适用于类的实例,因为类是引用类型。 它不适用于结构和枚举,因为它们是值类型。

    By default, class instance creates a strong reference with their methods, constants, variables, etc. means it will not be deallocated until strong reference remains. Sometimes it causes memory leaks and deadlocks, and we can avoid this by declaring weak reference or unowned reference.

    默认情况下,类实例使用其方法,常量,变量等创建一个强引用 。这意味着在保留强引用之前,它不会被释放。 有时它会导致内存泄漏死锁 ,我们可以通过声明弱引用或未拥有的引用来避免这种情况。

    Difference between weak reference and unowned reference

    弱引用和非引用之间的区别

    The weak reference is an optional type, which means weak reference will set to nil once the instance it refers to frees from memory.

    弱引用是可选类型,这意味着一旦弱引用引用的实例从内存中释放出来,它将设置为nil

    On the other hand, unowned reference is a non-optional type, it never will be set to nil and always have some value.

    另一方面, 无主引用是非可选类型,它永远不会设置为nil并始终具有一些值

    Weak References

    参考文献薄弱

    You can declare weak reference with the weak keyword before variable or property. The following example will explain how to use weak references.

    您可以在变量或属性之前使用weak关键字声明弱引用 。 下面的示例将说明如何使用弱引用

    Let’s implement class Animal with property name type of String and zoo which is an optional type.

    让我们使用属性name类型为String和zoo是可选类型的class Animal来实现class Animal类。

    class Animal {
    let name: String
    var zoo: Zoo? init(name: String) {
    self.name = name
    } deinit {
    print("\(name) is deinitialized.")
    }
    }

    Now, define another class Zoo with a property location type of String and animal as a weak reference which is an optional type.

    现在,使用属性位置类型为String定义另一个类Zoo 动物作为弱参考 ,这是可选类型。

    class Zoo {
    let location: String
    weak var animal: Animal?

    init(location: String) {
    self.location = location
    } deinit {
    print("Zoo at \(location) is deinitialized.")
    }
    }

    Define two variables tiger and logoaZoo of optional type and set to an instance. Then, link both instances together with the use of unwrap.

    定义两个可选类型的变量tigerlogoaZoo并将其设置为实例 然后,使用展开将两个实例链接在一起。

    var tiger: Animal? = Animal(name: "Amber")var lagoaZoo: Zoo? = Zoo(location: "11th St, Corona, NY 11368, United States")tiger!.zoo = lagoaZoo
    lagoaZoo!.animal = tiger

    When we set tiger variable to nil it breaks the strong reference to Animal instance and it deallocated from the memory.

    当我们将Tiger变量设置为nil时,它将破坏对Animal实例的强引用,并且将其从内存中释放。

    As there is no more strong reference to the Animal instance so animal property will also be set to nil.

    由于不再强烈引用Animal实例,因此animal属性也将设置为nil。

    tiger = nil

    If we set logoaZoo to nil it breaks strong reference to Zoo instance and will also deallocated from the memory.

    如果将logoaZoo设置为nil,它将破坏对Zoo实例的强引用,并且还将从内存中释放。

    lagoaZoo = nil

    Unowned References

    无人参考

    You can declare unowned reference with the unowned keyword before variable or property. The following example will explain how to use unowned references.

    您可以在变量或属性之前使用无主关键字声明无主引用 。 以下示例将说明如何使用未拥有的引用

    Define class Employee with the property name of type String and bank which is optional type.

    定义类Employee ,其属性名称类型为String和bank (可选类型)。

    class Employee {
    let name: String
    var bank: Bank? init(name: String) {
    self.name = name
    } deinit {
    print("\(name) is deinitialized.")
    }
    }

    Let’s define another class Bank with the property name of type String and employee which is unowned property (non-optional).

    让我们定义另一个类Bank ,其属性名称类型为String,而employee则为非拥有属性(非可选)。

    class Bank {
    let name: String
    unowned let employee: Employee init(name: String,employee: Employee) {
    self.name = name
    self.employee = employee
    } deinit {
    print("\(name) is deinitialized.")
    }
    }

    Define variables ana of optional type and set to an instance. Then, assign Bank instance to Employee’s bank property.

    定义可选类型的变量ana并将其设置为实例 然后,将银行实例分配给员工的银行财产。

    var ana: Employee? = Employee(name: "Ana")
    ana!.bank = Bank(name: "SPI Bank", employee: ana!)

    When we set ana to nil, there is no strong reference with Employee instance and it will be deallocated.

    当我们将ana设置为nil时, Employee实例没有强引用,它将被释放。

    Because there is no strong reference with Bank instance it will also be deallocated from memory.

    因为与Bank实例没有强引用,所以它也将从内存中释放。

    ana = nil

    Conclusion

    结论

    It’s useful to know how memory management works in Swift to break strong reference to instance and to avoid any memory leaks in the application.

    了解内存管理如何在Swift中工作以打破对实例的强引用并避免应用程序中的任何内存泄漏非常有用。

    To find newest tips and tricks about iOS development, Xcode and Swift please visit https://www.gurjit.co

    要查找有关iOS开发,Xcode和Swift的最新提示和技巧,请访问https://www.gurjit.co

    Thanks!

    谢谢!

    翻译自: https://levelup.gitconnected.com/difference-between-weak-and-unowned-references-explained-in-swift-24cb1d110650

    swift 弱引用

    展开全文
  • 近期看到swift里面不仅有循环引用和弱引用(weak),还加入了主引用(unowned),于是写了一些demo,这里总结一下。 和OC一样。Swfit默认也是基于ARC进行内存管理的,因此尽管简单,但假设不注意任然会出现循环...

    近期看到swift里面不仅有循环引用和弱引用(weak),还加入了无主引用(unowned),于是写了一些demo,这里总结一下。

    和OC一样。Swfit默认也是基于ARC进行内存管理的,因此尽管简单,但假设不注意任然会出现循环引用问题(Retain cycle),导致内存泄露。

    在OC中,能够非常easy的举出一个循环引用的样例。比方有两个类A和B,A中有一个属性是B类的实例,而B中又有一个属性是A类的实例。同一时候这两个属性都是strong的。这就导致了一个最简单的循环引用。

    可是由于swift语法的特殊性。这种样例不像OC中一样easy构造。

    由于对于一般类型的属性,Swfit要求在一个类的初始化方法中保证它一定有值。

    这将导致一个死循环。

    试想一下,A类在初始化的时候要保证它的某一个类型为B的属性先被初始化。而这个属性中又含有一个类型为A的属性须要先被初始化。

    这样循环下去的后果是,没有不论什么一个A或者B类的对象能先被初始化。假设同意代码的话,能够编译,可是执行时会报错:“EXC_BAD_ACCESS”.

    可是Swift这个特性并不意味着,在swift里面就不会出现引用循环问题了。

    由于swift还提供了可选类型,这个类型能够不被赋值,默认值就是nil(这个nil表示没有赋值。而不表示不论什么详细的值,在OC中nil表示空指针)。

    对于之前举得样例,仅仅要把属性设置为相应类的可选类型,一样会导致循环引用问题。

    与OC相似,解决循环引用问题最简单方法就是把属性定义为weak。比方

    class ClassA {
        weak var classBInstance: ClassB?
        init(){
            //初始化操作
        }
    }

    当弱引用所指向的对象被回收后,这个弱引用会自己主动被置为nil。

    这一点和OC非常相似。因此也能够看到,由于nil是可选类型的特权,所以weak修饰符仅能修饰可选类型属性。

    与OC不同的是,除了弱引用外。swift还提供了无主引用来打破引用循环。依据我们刚刚的讨论,导致循环引用的属性,至少有一个是可选类型。这也就是说。有可能在还有一个类里面。它的属性不是可选类型:

    class ClassB {
        unowned var classAInstance: ClassA = ClassA()
        init(){
            //初始化操作
        }
    }

    比方在B类中,classAInstance这个属性就能够不是可选类型。在这种情况下,还能够使用无主引用来打破引用循环。语法就是把weak替换为unowned关键字。unowned属性引用的对象被回收后,引用不会被置为nil,也不能被訪问,否则会触发执行时错误。

    总结一下就是:

    1. 和OC一样。Swift也是用ARC,也会有循环引用导致内存泄露
    2. 假设属性是可选类型。仅仅能用weak修饰符避免循环引用。所引用对象被回收后改属性会被自己主动置为nil
    3. 假设属性不是可选类型,仅仅能用无主引用(unowned)。所引用对象被回收后属性不会被置为nil,此时訪问会导致执行时错误。相似OC中的unsafe_unretained修饰符

    附录

    查看完整专栏——《Swift轻松入门》

    【Swift入门(一)——基本的语法】
    【Swift入门(二)——字符与字符串】
    【Swift入门(三)——元组(Tuple)】
    【Swift入门(四)——可选类型(Optionals)与断言(Assert)】
    【Swift入门(五)——数组(Array)】
    【Swift入门(六)——字典(Dictionary)】
    【Swift入门(七)——结构体(Struct)】
    【Swift入门(八)——功能强大的求余运算符】
    【Swift入门(九)——String与Int、Double、Float等数字相互转换】
    【Swift入门(十)——循环引用、弱引用和无主引用】
    【Swift入门(十一)——类型转换与is、as操作】
    【Swift入门(十二)——利用Extension加入逆序输出字符串方法】

    转载于:https://www.cnblogs.com/jzssuanfa/p/7236379.html

    展开全文
  • Swift 提供了两种解决循环引用的方法,弱引用和无主引用。 弱引用和无主引用可以使循环中的一个实例引用另一个实例时不使用强引用。 1、弱引用 对生命周期中会变为 nil 的实例采用弱引用,也就是说对可选类型采用...

    前言

    • Swift 提供了两种解决循环引用的方法,弱引用和无主引用。

    • 弱引用和无主引用可以使循环中的一个实例引用另一个实例时不使用强引用。

    1、弱引用

    • 对生命周期中会变为 nil 的实例采用弱引用,也就是说对可选类型采用弱引用。

    • 声明一个弱引用的关键字为 weak

    1.1 弱引用示例

    • 比如存在一个学生类,一个班级类,学生类有一个属性叫学生所属的班级,而班级有一个属性是班长,它引用了一个学生类,它们都是可选型。

      class Student {
          var name: String?
          weak var theClass: Class?
      
          deinit {
              print("Student deinit")
          }
      }
      
      class Class {
          var name: String?
          weak var classMonitor: Student?
      
          deinit {
              print("Class deinit")
          }
      }
      var sneb: Class? = Class()
      sneb?.name = "sannianerban"
      
      var xiaoming: Student? = Student()
      xiaoming?.name = "xiaoming"
      
      sneb?.classMonitor = xiaoming
      xiaoming?.theClass = sneb
    • 因为班长对小明的引用为弱引用,不会算作引用计数,所以此时实例 xiaoming 的引用计数为 0,调用析构器释放内存空间,现在可以自由释放 sneb 这个实例的空间了。

      sneb = nil
      xiaoming = nil

    2、无主引用

    • 对初始化后再也不会变为 nil 的实例采用无主引用,也就是说对非可选类型采用无主引用。

    • 声明一个无主引用的关键字为 unowned

    2.1 无主引用示例

    • 修改上例中的属性。

      class Class {
          var name: String?
          unowned var classMonitor = Student()
      
          deinit {
              print("Class deinit")
          }
      }
    展开全文
  • Swift 使用自动引用计数(ARC)机制来追踪管理你的 App 的内存。在大多数情况下,这意味着 Swift的内存管理机制会一直起作用,你不需要自己考虑内存管理。当这些实例不再需要时,ARC会自动释放类实例所占用的内存。 ...

    Swift 使用自动引用计数(ARC)机制来追踪和管理你的 App 的内存。在大多数情况下,这意味着 Swift内存管理机制一直起作用,你不需要自己考虑内存管理。当这些实例不再需要时,ARC自动释放类实例所占用的内存。
    Swift中使用 ARC 在 Objective-C 中使用 ARC 十分相似。

    类实例才拥有引用计数,结构体枚举这种值类型不是引用类型,没有引用计数。

    ARC的工作机制

    每次你创建一个类的实例,ARC 会分配一大块内存来存储这个实例的信息。这些内存中保留有实例的类型信息,以及该实例所有存储属性值的信息。
    此外,当实例不需要时,ARC 会释放该实例所占用的内存,释放的内存用于其他用途。这确保类实例当它不在需要时,不会一直占用内存。
    然而,如果 ARC 释放了正在使用的实例内存,那么它将不会访问实例的属性,或者调用实例的方法。确实,如果你试图访问该实例,你的app很可能会崩溃
    为了确保使用中的实例不会消失,ARC跟踪计算当前实例被多少属性常量变量所引用。只要存在对该类实例的引用,ARC 将不会释放该实例。
    为了使这些成为可能,无论你将实例分配给属性常量变量,它们都会创建该实例的强引用。之所以称之为“强”引用,是因为它会将实例保持住,只要强引用还在,实例是不允许被销毁的。

    ARC

    下面的例子展示了自动引用计数的工作机制。这个例子由一个简单的 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 类定义了一个构造函数析构函数用来打印信息。

    下面的代码片段定义了三个 Peroson? 类型的变量,用来按照代码中的顺序,为新的 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 到该实例的强引用。因为至少有一个强引用,ARC可以确保 Person 一直保持在内存中不被销毁。

    如果你将同一个 Person 实例分配给了两个变量,则该实例又会多出两个强引用:

    reference2 = reference1
    reference3 = reference1
    

    现在这一个 Person 实例就有了三个强引用。

    如果你通过给其中两个变量赋值 nil的方式断开两个强引用,只留下一个强引用, Person 实例不会被销毁:

    reference1 = nil
    reference2 = nil
    

    在你清楚地表明不再使用这个 Person实例时,直到第三个也就是最后一个强引用被断开时ARC销毁它。

    reference3 = nil
    *// prints “John Appleseed is being deinitialized”*
    

    类实例之间的循环强引用

    这段内容很长,简单总结一下就是:
    实例互相持有会产生循环强引用,并且变量置空无法破除这种强引用。
    能理解这一点,可以跳过不看下面的内容。

    写出某个类永远不会变成零强引用的代码是可能的。

    如果两个类实例彼此持有对方的强引用,因而每个实例都让对方一直存在,就会发生这种情况。这就是所谓的循环强引用

    解决循环强引用问题,可以通过定义类之间的关系为弱引用( weak )或无主引用( unowned )来代替强引用。

    例子

    下面的例子展示了一个如何意外地创建循环强引用的例子。这个例子定义了两个类,分别是 PersonApartment ,用来建模公寓和它其中的居民:

    class Person {
        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 }
        var tenant: Person?
        deinit { print(Apartment \(unit) is being deinitialized”) }
    }
    

    每一个 Person 实例有一个类型为 String,名字为 name 的属性,并有一个可选的初始化为 nilapartment属性。 apartment 属性是可选项,因为一个人并不总是拥有公寓。

    类似的,每个 Apartment 实例都有一个叫 unit ,类型为 String 的属性,并有一个可选的初始化为 niltenant 属性。 tenant 属性是可选的,因为一栋公寓并不总是有居民。

    这两个类都定义了析构函数,用以在类实例被释放时输出信息。这让你能够知晓 PersonApartment 的实例是否像预期的那样被释放。

    定义变量然后赋值:

    var John: Person?
    var unit4A: Apartment?
    
    John = Person(name:John Appleseed)
    unit4A = Apartment(unit: “4A”)
    

    引用关系:
    在这里插入图片描述

    赋值,!用来展开访问可选项

    John!.apartment = unit4A
    unit4A!.tenant = John
    

    然后就变成了互相强引用:
    在这里插入图片描述

    这两个实例关联后会产生一个循环强引用。

    Person 实例现在有了一个指向 Apartment 实例的强引用,而 Apartment 实例也有了一个指向 Person 实例的强引用 我的理解:强引用存在于两个实例之间,而不是两个变量之间。因此,当你断开 johnunit4A 变量所持有的强引用时,引用计数并不会降零,实例也不会被 ARC 释放:

    John = nil
    unit4A = nil
    

    当我们把这两个变量设为 nil 时,没有任何一个析构函数被调用。循环强引用会一直阻止 PersonApartment 类实例的释放,这就在你的应用程序中造成了内存泄漏
    在我们将 johnunit4A 赋值为 nil 后,强引用关系如下图:

    在这里插入图片描述

    PersonApartment 实例之间的强引用关系保留了下来并且不会被断开。

    破除实例之间的循环强引用

    Swift 提供了两种办法用来解决我们在使用类的属性时所遇到的循环强引用问题:弱引用( weak reference )和无主引用( unowned reference )。

    弱引用无主引用允许循环引用中的一个实例引用另外一个实例而不保持强引用。这样实例能够互相引用而不产生循环强引用

    弱引用

    弱引用不会对其引用的实例保持强引用,因而不会阻止 ARC 释放被引用的实例。这个特性阻止了引用变为循环强引用。声明属性或者变量时,在前面加上 weak 关键字表明这是一个弱引用
    由于弱引用不会强保持对实例的引用,所以说实例被释放了弱引用仍旧引用着这个实例也是有可能的。因此,ARC 会在被引用的实例被释放时自动地设置弱引用为 nil 。由于弱引用需要允许它们的值为 nil ,它们一定得是可选类型
    你可以检查弱引用的值是否存在,就像其他可选项的值一样,并且你将永远不会遇到“野指针”。

    在 ARC 给弱引用设置nil时不会调用属性观察者

    例子

    下面的例子跟上面 PersonApartment 的例子一致,但是有一个重要的区别。这次, Apartmenttenant 属性被声明为弱引用:

    class Person {
        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: Person?
        deinit { print(Apartment \(unit) is being deinitialized”) }
    }
    

    两个变量( johnunit4A )之间的强引用关联创建得与上次相同:

    var John: Person?
    var unit4A: Apartment?
     
    John = Person(name:John Appleseed)
    unit4A = Apartment(unit: “4A”)
     
    John!.apartment = unit4A
    unit4A!.tenant = John
    

    现在,两个关联在一起的实例的引用关系如下图所示:

    在这里插入图片描述

    断开john变量的强引用,Person对象被释放:

    John = nil
    *// prints “John Appleseed is being deinitialized”*
    

    在这里插入图片描述

    现在只剩下来自 unit4A 变量对 Apartment 实例的强引用。再打断这个强引用,那么 Apartment实例就再也没有强引用了:

    unit4A = nil
    *// prints “Apartment 4A is being deinitialized”*
    

    没有指向 Apartment 实例的强引用,该实例得到释放

    在这里插入图片描述

    总结:当最后一个强引用被破除时,对象将会被释放

    无主引用

    和弱引用类似,无主引用不会牢牢保持住引用的实例。但是不像弱引用,总之,无主引用假定是永远有值的。因此,无主引用总是被定义为非可选类型。你可以在声明属性或者变量时,在前面加上关键字 unowned 表示这是一个无主引用。

    由于无主引用是非可选类型,你不需要在使用它的时候将它展开。无主引用总是可以直接访问。不过 ARC 无法在实例被释放后将无主引用设为 nil ,因为非可选类型的变量不允许被赋值为 nil

    如果你试图访问引用的实例已经被释放了的无主引用Swift 会确保程序直接崩溃。

    例子

    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”) }
    }
    

    定义一个可选Customer变量,声明为可选项,所以被初始化为nil

    var John: Customer?
    

    初始化一个 Customer 实例,分配一个新的 CreditCard 实例作为 customercard 属性:

    John = Customer(name:John Appleseed)
    John!.card = CreditCard(number: 1234_5678_9012_3456, customer: John!)
    

    在这里插入图片描述

    现在 Customer 实例对 CreditCard 实例有一个强引用,并且 CreditCard 实例对 Customer 实例有一个无主引用

    断开john变量的引用

    John = nil
    *// prints “John Appleseed is being deinitialized”*
    *// prints “Card #1234567890123456 is being deinitialized”*
    

    由于 Customer无主引用,当你断开john 变量持有的强引用时,那么就再也没有指向 Customer 实例的强引用了,Customer实例会被释放,紧接着CreditCard实例也会被释放。
    在这里插入图片描述

    不安全的无主引用

    使用unowned(unsafe)来明确使用了一个不安全无主引用。如果你在实例的引用被释放后访问这个不安全无主引用,你的程序就会尝试访问这个实例曾今存在过的内存地址,这就是不安全操作。

    无主可选引用

    你可以给类标记可选引用来作为无主引用。从 ARC 所有权模型角度来看,无主可选引用弱引用可在同一个上下文中使用。区别在于使用无主可选引用时,你需要负责保证它总引用到一个合法对象nil

    例子:

    unowned var nextCourse: Course?
    

    无主可选引用并不保持它包含的对象的强引用,所以它并不会阻止 ARC 释放实例。它的行为和无主引用ARC 下一致,除了无主可选引用能是 nil

    无主引用和隐式展开的可选属性

    var capitalCity: City!
    

    通过在类型结尾处加上感叹号 City! 的方式,以声明 CountrycapitalCity 属性为一个隐式展开的可选属性。capitalCity 属性有一个默认值 nil,但是不需要展开它的值就能访问它。

    你可以通过一条语句同时创建 CountryCity 的实例,而不产生循环强引用,并且 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 属性在初始化完成后,就能像非可选项一样使用和存取,同时还避免了循环强引用。

    闭包的循环强引用

    闭包和实例之间可能会产生循环强引用。

    Swift 提供了一种优雅的方法来解决这个问题,称之为闭包捕获列表( closuer capture list )。

    例子

    class HTMLElement {
        
        let name: String
        let text: String?
        
        lazy var asHTML: () -> 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”)
        }
        
    }
    

    实例化:

    var paragraph: HTMLElement? = HTMLElement(name: “p”, text: “hello, world”)
    print(paragraph!.asHTML())
    *// prints”hello, world”*
    

    在这里插入图片描述

    闭包在其闭包体内使用了 self (引用了 self.name 和 self.text ),因此闭包捕获self ,这意味着闭包又反过来持有了 HTMLElement 实例的强引用。这样两个对象就产生了循环强引用。

    如果设置 paragraph 变量为 nil ,不能打破这种循环强引用,闭包和实例都没有被释放:

    paragraph = nil
    

    解决闭包的循环强引用

    定义捕获列表

    捕获列表中的每一项都由 weakunowned 关键字与类实例的引用(如 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: () -> String = {
        [unowned self, weak delegate = self.delegate!] in
        *// closure body goes here*
    }
    
    弱引用和无主引用

    如果被捕获的引用永远不会变为 nil ,应该用无主引用而不是弱引用
    前面的 HTMLElement 例子中,无主引用是正确的解决循环强引用的方法。这样编写 HTMLElement 类来避免循环强引用:

    class HTMLElement {
        
        let name: String
        let text: String?
        
        lazy var asHTML: () -> 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 赋值为 nilHTMLElement 实例将会被释放,并能看到它的析构函数打印出的消息。

    paragraph = nil
    *// prints “p is being deinitialized”*
    

    Swift学习群

    欢迎加入本人的Swift学习微信群,一同互相监督学习,我微信:reese90

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 10
收藏数 200
精华内容 80
关键字:

弱引用和无引用