strong、weak和unowned的区别

  • 时间:2022-03-15 14:35 作者:pro648 来源: 阅读:306
  • 扫一扫,手机访问
摘要:编写代码时需注意能否产生了循环引用,因而就产生了什么时候使用weak、unowned问题?这篇文章将详情 Swift 中的strong、weak、unowned的区别。1. ARC自动引用计数(即 Automated Reference Count,简称 ARC)是 Xcode 4.2版本的新特性,

编写代码时需注意能否产生了循环引用,因而就产生了什么时候使用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

  • 全部评论(0)
最新发布的资讯信息
【系统环境|】2FA验证器 验证码如何登录(2024-04-01 20:18)
【系统环境|】怎么做才能建设好外贸网站?(2023-12-20 10:05)
【系统环境|数据库】 潮玩宇宙游戏道具收集方法(2023-12-12 16:13)
【系统环境|】遥遥领先!青否数字人直播系统5.0发布,支持真人接管实时驱动!(2023-10-12 17:31)
【系统环境|服务器应用】克隆自己的数字人形象需要几步?(2023-09-20 17:13)
【系统环境|】Tiktok登录教程(2023-02-13 14:17)
【系统环境|】ZORRO佐罗软件安装教程及一键新机使用方法详细简介(2023-02-10 21:56)
【系统环境|】阿里云 centos 云盘扩容命令(2023-01-10 16:35)
【系统环境|】补单系统搭建补单源码搭建(2022-05-18 11:35)
【系统环境|服务器应用】高端显卡再度登上热搜,竟然是因为“断崖式”的降价(2022-04-12 19:47)
手机二维码手机访问领取大礼包
返回顶部