SWIFT中的引用关系说明

更新时间:2023-07-14 15:44:27 阅读: 评论:0

七彩溶洞
SWIFT中的引用关系说明
我发现自己写代码的时候经常担心强引用循环(retain cycles)的出现。我觉得这个和其他问题一样比较常见。不知道你是什么情况,我反正总是听见"我什么时候要用关键词weak?'unowned'这坨东西到底是啥玩意儿?"这类声音。我们发现的问题是我们知道在swift代码中要去用strong,weak和unowned说明符来避免强引用循环,但是我们不大了解具体用哪一个。好在我知道它们是啥,还知道啥时候去用他们!希望这篇文章能教会你知道什么时候,并且在哪里用这3个说明符。
一单一双眼皮叫什么眼咱们开始吧
ARC
ARC是自动内存管理Apple版本的一个编译时特性(compile time feature)。全称是Automatic Reference Counting。意思是对于一个对象来说,只有在没有任何强引用指向它时,该对象占用的内存才会被回收。
STRONG-强引用
从什么是强引用说起。它本质上是一个普通的引用(指针或者其他有相同意思的东西),但是它特殊在能够通过将该引用指向对象(object)的保留计数(retain count)增加1来保护这个对象不被ARC回收。
实质上,哪怕任何一个东西的一个强引用指向了这个对象,这个对象就不会被回收。记住这点,待会儿讲强引用循环和相关东西的时候会用到。
强引用在swift中几乎随处可见。实际上声明一个属性(property)的时候默认就是一个强引用。通常在关系层级是线性的时候用强引用问题不大。当强引用从父层级流向子层级的时候,这个强引用的使用总是没问题。
这有个强引用的例子。
1.class Kraken{
爱出猫2.let tentacle=Tentacle()//对子层级的强引用。
3.}
4.class Tentacle{
5.let sucker=Sucker()//对子层级的强引用。
6.}
7.class Sucker{}
8.
9.*/Kraken的意思是海妖,Tentacle的意思是触手,sucker的意思是
吸盘...译者注/*
例子中是一个线性的关系层级。Kraken有一个指向Tentacle实例的强引用,Tentacle实例又有一个指向Sucker实例的强引用。引用关系的流向从父层级(Kraken)一直向下流到子层级(Sucker)。
在animation block里引用层级也是类似的:
1.UIView.animateWithDuration(0.3){
中国银行存款2.lf.view.alpha=0.0
3.}
因为animateWithDuration是UIView的一个静态方法,这里的闭包是父层级,lf是子层级。
如果子层级想引用父层级怎么办?这就是我们要用弱引用和unowned引用的地方。
WEAK AND UNOWNED REFERENCES-弱引用和UNOWNED引用
WEAK-弱引用
弱引用就是一个保护不了其所指对象不被ARC回收的指针。强引用能让它对象的保留计数增加1,弱引用不能。
swift中,所有的弱引用都是非常量的可选类型(non-constant Optionals)(想一下var和let的关系),因为在没有其他强引用指向的时候,这个引用能,并且会被改变成nil。
例如下面的代码就不能通过编译:
1.class Kraken{
2.weak let tentacle=Tentacle()//let是一个常量。所
有的weak变量都必须是可变(mutable)的。
3.}
因为tentacle是一个let常量。Let由于规范限制使得其在运行时不能够被改变。因为弱引用变量(weak
variables)在没有任何强引用指向它们时是会被改变成nil的,所以swift编译器要求你将弱引用变量声明成var。
那些会出现潜在的强引用循环的地方就是使用弱引用变量的关键之处。强引用循环发生在两个对象彼此之间都用强引用指向对方的情况下,ARC不会对其中任何一个实例发出正确的释放信号代码(relea message code),因为这两个实例正彼此保护着对方。这有个来自Apple的简洁图片,非常明了的展示了这点:
下面是一个能展示强引用循环的很棒的例子,其中用到了NSNotification API (还是比较新的API)。看看下面的代码吧:
1.class Kraken{
2.var notificationObrver:((NSNotification)->V
oid)?
3.init(){notificationObrver=NSNotificationCen
ter.defaultCenter().addObrverForName("humanEntered KrakensLair",object:nil,queue:NSOperationQueue.m ainQueue()){notification in
4.lf.eatHuman()
时间如流水
5.}
6.}
7.deinit{
轴对称图形教案8.if notificationObrver!=nil{
9.NSNotificationCenter.defaultCenter.r
emoveObrver(notificationObrver)
10.}
11.}
12.}
到这儿我们就搞出了一个强引用循环。你看,swift里的闭包与Objective-C里的blocks极像。如果一个变量是在闭包外面声明的,在闭包里面引用这个变量就会产生出另一个强引用。此种情况下仅有的例外就是使用值类型的变量,比如swift里的Ints,Strings,Arrays和Dictionaries。
草原一枝花
这里NSNotificationCenter保留了一个闭包,当你调用eatHuman()方法时这个闭包以强引用的方式捕获了lf。问题是:我们直到deinit的时候才清空这个闭包,但是deinit永远不会被ARC调用,因为这个闭包有一个对Kraken实例的强引用!
用NSTimers和NSThread的地方也会出现这种情况。
解决方法是在闭包的捕获列表(capture list)里使用一个对lf的弱引用。这就打破了强引用循环。到了这里,我们的对象引用关系图就变成了这样:
把lf变成weak不会给lf的保留计数加1,这就能让ARC在正确的时间将其合理的销毁。
要在闭包里使用weak和unowned变量的话,需要在闭包体内用[]语法。例如:1.let closure={[weak lf]in
2.lf?.doSomething()//记住,所有的weak变量都是可选类
型。
3.}
为什么weak lf会在方括号里?这看起来很怪!Swift中我们看见方括号就会想到数组。你猜怎么着?你可以在闭包里指定多个待捕获的值!比如:
1.let closure={[weak lf,unowned krakenInstance]
in//瞧这个捕获了多个值的数组
2.lf?.doSomething()//weak变量是可选类型
3.krakenInstance.eatMoreHumans()//unowned变量不是
可选类型
4.}
看起来就像数组多了吧?现在你就知道了为什么捕获值是写在方括号里的。好,用我们现在所学到的,在上面notification代码的闭包捕获列表中加上[weak lf]就可以解决强引用循环的问题:
1.NSNotificationCenter.defaultCenter().addObrverForN
ame("humanEnteredKrakensLair",object:nil,queue:N
SOperationQueue.mainQueue()){[weak lf]notificat
ion in//使用捕获列表消除了强引用循环!
2.lf?.eatHuman()//lf现在是一个可选类型了!
3.}
林清玄散文集
用到weak和unowned变量的另外一个地方就是使用协议(protocol)在多个class间去实现委托(delegation)的情况,因为swift中class是引用类型。结构体(structs)和enum(枚举)也能遵循协
议,但是它们是值类型。如果一个父类带上一个子类使用委托,像这样:
1.class Kraken:LossOfLimbDelegate{
2.let tentacle=Tentacle()
3.init(){
5.}
6.func limbHasBeenLost(){
7.startCrying()

本文发布于:2023-07-14 15:44:27,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/82/1096360.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:引用   循环   对象   时候
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图