GeekBand·iOS--Retain Cycle(引用循环)那些事
来源:互联网 发布:数据库物理结构包括 编辑:程序博客网 时间:2024/05/15 23:53
Retain Cycle:
Retain Cycle是如何形成的呢?
我们知道当一个父对象(主动方)持有子对象(被动方)时,子对象会随着父对象的消亡而消亡
但是假若两个对象互为父对象呢?会如何?
对,这样子,一个retain cycle就形成了,当然,互为父对象的引用都需要是强引用。
举个简单的例子:
就比如在一般的delegate情况下,一般都是子对象将delegate设为父对象,所以为了防止引用循环,delegate通常都是弱引用(当然也有不一般的情况,delegate的对象不是父对象,这种情况下设delegate为强引用没有问题,但由于delegate不可能是子对象,所以生命周期可以控制得比委托代理的对象的生命周期要长,哪怕是短也没问题,因为delegate是一个弱引用,而弱引用指针会自动置nil,至于能否在调用delegate方法时确定delegate对象依然存在需要靠程序员自己来控制delegate对象的生命周期了)
看不懂?让我用代码来举个更简单例子(MRR——Mutual Retain Release环境下):
第一个测试类
第二个测试类
这样,引用循环就形成了,造成的结果就是,子对象对父对象的引用无法再取得,导致两个对象被隐式引用,最后两个test实例的引用计数都是1,最后无法释放。
程序输出结果:
但是其实,这里有个小问题,苹果官网说了,retain count不能作为一个判断对象是否释放的标准。
所以这里,最好的用来判断对象是否释放的方法,是判断对象是否调用了自身的dealloc方法,在dealloc方法中添加log或者加断点可以轻易地发现,对象并没有调用dealloc。
如何解决Retain Cycle?
将子对象(被动方)对另一父对象——当然可以是自己,代码如下:
@interface Obj : NSObject@property(strong, nonatomic) id someObj;@endint main(){ Obj *obj = [[Obj alloc] init]; obj.someObj = obj; return 1;}
在父对象生命周期结束(获取不到父对象的引用)前将子对象置nil或者release。
Block造成的引用循环
在编码中,由于block可以引用外部环境,于是我们对block的使用不慎,也可能导致引用循环,下面让我来分析一下在MRR以及ARC环境下,由block所引起引用循环
MRR
假设我有一个manager 实例,实例中有一个block的属性
block运行时,会隐性retain它所用到的变量
代码如下:
DoSomethingManager *manager = [[DoSomethingManager alloc] init];manager.complete = ^{ //...complete actions [manager otherAction]; [manager release];};
这样子就造成 manager 和它的complete block相互持有,导致引用循环。
正确的做法应该是,在block中把manager里的complete block置nil,再释放自己,就可以破坏引用循环。
代码如下:
DoSomethingManager *manager = [[DoSomethingManager alloc] init];manager.complete = ^{ //...complete actions [manager otherAction]; manager.complete = nil; [manager release];};
ARC
在ARC中,在block中也会引入retain cycle,只是解决方法除了上面所提到的办法外,还有别的方法解决以及预防block中的retain cycle
比如:
在ARC中,引入了三个新的所有权修饰符
1. __strong : 修饰的变量会自动被retain一次,并且在block中也会被retain一次2. __unsafe_unretained:修饰的变量不会被retain,但在block运行中无法控制它的生命周期,可能block在运行过程中,它已经被释放了,留下一个野指针3. __weak:修饰的变量一样不会被retain,它修饰的变量的生命周期同样无法被block控制,但是好在它修饰的指针指向的内存在被系统释放后,它会置nil,这样就会安全许多。
在这里提一下,id类型和对象类型的所有权修饰符默认为__strong修饰符。即我们随手创建的对象,只要不特别声明,就是一个强引用。
事实上,还有两个所有权修饰符——即:
- __autoreleasing,但是这个修饰符非本文所要讨论的范围。
- __block,等下我们再来讨论这个修饰符
说回解决上述问题的retain cycle的新方法(ARC中):
DoSomethingManager *manager = [[DoSomethingManager alloc] init];// manager actions__weak DoSomethingManager *weakManager = manager;manager.complete = ^{ //...complete actions [weakManager otherAction]; };
接下来我们来看一个新的解决block中的引用循环的办法吧!(用到__block关键字)
__block DoSomethingManager *manager = [[DoSomethingManager alloc] init];manager.complete = ^{ //...complete actions [manager otherAction]; manager = nil;};
如果不用ARC,manager不会在block中被retain,但是在ARC中,情况就有点复杂了。由于__block变量保存更为底层的地址,那么,当__block变量指向别的对象的时候,引发的情况就是,block不再对原来的变量负责,导致之前的对象被release掉,而retain cycle就被破坏了。
下面附上两篇关于retain cycle的好文:
- http://www.csdn.net/article/2015-05-27/2824782-demystifying-retain-cycles-in-arc
- http://swifter.tips/retain-cycle/
- GeekBand·iOS--Retain Cycle(引用循环)那些事
- block基础和retain cycle(循环引用)
- reference cycle 引用循环
- 探讨OC的内存管理 以及防止循环引用retain cycle 代理为什么用weak block为什么用copy
- iOS中的block和retain cycle (经典)
- IOS中的block和retain cycle
- IOS中的block和retain cycle
- IOS中的block和retain cycle (经典)
- iOS中的block和retain cycle (经典)
- iOS 中 retain cycle 的产生
- IOS中的block和retain cycle (经典)
- ios之block和retain cycle (经典)
- IOS中的block和retain cycle (经典)
- IOS中的block和retain cycle
- IOS中的block和retain cycle
- iOS中的block和retain cycle
- IOS中的block和retain cycle
- IOS中的block和retain cycle
- hdu 1282 回文数猜想
- Word Break II
- const、volatile、mutable关键字
- AFNetworking 上传多张图片 xml 解析方式
- Redis双链表实现安全队列
- GeekBand·iOS--Retain Cycle(引用循环)那些事
- 泛型(二)
- vector中erase()方法详解
- python学习第二天
- adb命令使用之抓取log并过滤
- & 和 && 的区别
- 命令行编译、运行java
- Eclipse java工程结构,单元测试创建
- Android 进程间通信-Intent、Messenger、AIDL