strong、weak和unowned的区别

作者 : 开心源码 本文共3875个字,预计阅读时间需要10分钟 发布时间: 2022-05-14 共208人阅读

编写代码时需注意能否产生了循环引用,因而就产生了什么时候使用weakunowned问题?这篇文章将详情 Swift 中的strongweakunowned的区别。

1. ARC

自动引用计数(即 Automated Reference Count,简称 ARC)是 Xcode 4.2版本的新特性,其与手动管理内存使用了相同的计数系统。不同点在于:系统在编译时会帮助我们插入合适的内存管理方法,保留和释放都会自动进行,避免了手动管理引用计数的少量潜在问题。

Swift 使用自动引用计数跟踪、管理app的内存。通常情况下,这意味着ARC会自动管理内存,开发者无需关注内存管理。当类的实例不再使用时,ARC会自动释放其占用的内存。

为帮助管理内存,ARC 有时需理解类之间的关系。在 Swift 中使用 ARC 与在 Objective-C 中使用 ARC 相似。

引用计数只适用类的实例。结构体和枚举是值类型,不是引用类型,存储和传递的时候并非使用引用。

2. strong

strong指针通过添加指向对象的引用计数,保护被指向对象不被ARC释放。即,只需有一个强指针指向该对象,它就不会被释放。

Swift 中公告的属性默认是strong。当对象间引用关系是线性时,使用strong指针不会产生问题。

当两个实例使用强指针指向彼此时,两个实例引用计数都不会变为零,即产生循环引用(strong reference cycle)。

下面是一个循环引用的示例:

class Person {    let name: String    init(name: String) {        self.name = name    }        var apartment: Apartment?    deinit {        print("\(name) is being deinitizlized")    }}class Apartment {    let unit: String    init(unit: String) {        self.unit = unit    }        var tenant: Person?    deinit {        print("Apartment \(unit) is being deinitialized")    }}

上面定义了两个类:PersonApartment,代表住户和公寓。两个类都实现了deinitializer方法,当类的实例销毁时进行打印,方便观察实例占用的内存能否释放了。

下面代码定义了两个可选类型的变量,初始值为nil。并为其分配两个新的实例:

var john: Person?var unit4A: Apartment?john = Person(name: "John Appleseed")unit4A = Apartment(unit: "4A")

目前,john变量强引用Person实例,unit4A变量强引用Apartment实例,如下所示:

UnownedReferenceCycle01.png

现在连接两个实例。person持有apartmentapartment持有person

john?.apartment = unit4Aunit4A?.tenant = John

连接两个实例后,引用关系如下:

UnownedReferenceCycle02.png

Person的实例强引用了Apartment的实例,Apartment的实例强引用了Person的实例,即产生了循环引用。当移除johnunit4A的引用时,实例的引用计数不会变为零,也就是实例内存不会被ARC释放。

john = nilunit4A = nil

设置johnunit4A变量为nil后,引用关系如下:

UnownedReferenceCycle03.png

PersonApartment实例之间的强引用无法破除。

3. 处理循环引用

Swift 提供了两种处理循环引用的方案:weakunownedweakunowned引用其它实例时不会产生强引用,引用计数不会加一。因而,不会产生循环引用。

当一个实例的生命周期短于另一个时(即一个实例可以先被销毁),使用weak引用。在上面公寓的示例中可能出现公寓没有住户的情况。因而,可以使用weak处理循环引用问题。当另一个实例生命周期与当前实例相同,或者长于当前实例时,使用unowned引用。

3.1 weak引用

weak引用不会强持有引用的实例,也就不会阻止ARC释放实例。通过在公告属性、变量前增加weak关键字的方式使用弱引用。

当实例被销毁时,ARC 会自动设置弱指针为nil。因为弱指针在运行时可能被设置为nil,弱指针应被公告为可选类型的变量,而非常量。

和其它可选类型一样,可以检查弱引用值能否存在,这样就不会得到一个无效实例。

设置弱引用为nil时,不会调用属性观察器。

使用weak修饰之前实例Apartment中的tenant属性,升级后如下:

class Person {    let name: String    init(name: String) {        self.name = name    }        var apartment: Apartment?    deinit {        print("\(name) is being deinitizlized")    }}class Apartment {    let unit: String    init(unit: String) {        self.unit = unit    }        // 使用weak修饰    weak var tenant: Person?    deinit {        print("Apartment \(unit) is being deinitialized")    }}

下图是实例间引用关系:

UnownedWeakReference01.png

Person实例强引用Apartment实例,但Apartment实例没有强引用Person实例。当移除john实例对Person的强引用,Person实例就没有被强引用了,也即可以被销毁了。

3.2 unowned

weak一样,unowned指针也不会对指向的对象产生强引用,但unowned用在另一个实例生命周期一样或者更长的情况。通过在公告属性、变量前增加unowned关键字的方式使用unowned

weak不同,unowned修饰的引用永远不为空。因而,标记为unowned的值不是可选类型,ARC 也不会将unowned引用设置为nil

只有确信引用不会被释放的时候才使用unowned,使用unowned修饰的对象被销毁后再次访问会产生运行时错误。

现在定义两个类:CustomerCreditCardCustomer是银行的用户,CreditCard是该用户的银行卡。CustomerCreditCard类都有一个属性持有彼此,这种持有关系会产生强引用。

CustomerCreditCard的关系与PersonApartment的关系稍有不同。Customer可能持有CreditCard,也可能不持有CreditCard;但CreditCard不会脱离Customer而存在。即Customer有一个可选类型的card属性,CreditCard有一个 unowned 的customer属性。

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实例,并使用该实例创立CreditCard,如下所示:

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

其引用关系如下:

UnownedUnownedReference01.png

Customer实例强引用CreditCard实例,CreditCard实例 unowned Customer实例。

john变量取消对实例的强引用后,就没有强引用指向该实例,该实例就会被销毁。该实例销毁后,没有强引用指向CreditCard,其也会被销毁。

上面的示例详情了如何使用 safe unowned 引用,Swift 同时提供了 unsafe unowned 引用,其可以避免 runtime 的安全检查,提高性能。使用 unsafe 相关操作时,开发者需自行检查其能否存在,确保安全。

使用unowned(unsafe)标记 unsafe unowned 引用。当实例销毁后,再次访问实例会直接访问销毁前的内存地址。

参考资料:

  1. What is the difference in Swift between ‘unowned(safe)’ and ‘unowned(unsafe)’?
  2. Automatic Reference Counting

欢迎更多指正: pro648/tips

本文地址: pro648/tips/blob/master/sources/strong%E3%80%81weak%E5%92%8Cunowned%E7%9A%84%E5%8C%BA%E5%88%AB.md

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » strong、weak和unowned的区别

发表回复