iOS循环引用梳理

总结梳理一下循环引用相关的知识点。

循环引用分三种:1.自循环引用,2.相互循环引用,3.多循环引用,这三种就不多介绍了,是个程序员都明白。

循环引用出现的地方多数是在,block,NSTimer中,代理中如果代理对象没有设置为weak也会产生循环引用。

破环的方法无非是将一方引用的方式改为弱引用,但在OC中,引用一个对象而不增加其引用计数一共有三种关键字可以实现:1.weak,2.block,3._unsafe_unretained

第一种weak之前的文章已经详细叙述了其工作原理,如何对对象进行添加弱引用指针以及弱引用指针如何在对象被销毁时进行回收的,这里就不多写了。

第二种__block关键字,其在MRC模式下,使用__block关键字修饰一个对象,不会增加其引用计数,从而可以避免循环引用。但是在ARC中,__block关键字修饰的对象会被强引用,就没法避免循环引用了。这就是__block和__weak的区别

第三种_unsafe_unretained关键字,使用之后确实不会增加引用对象的引用计数,但是当引用对象被释放的时候,会产生悬垂指针,从而发生一些不可预见的错误,所以是不安全的,不可取。

以上就是这三种关键字的区别了。

这里总结一个最常见的循环引用案例——NSTimer

在NSTimer被创建的时候,由于系统常驻的runloop对其进行强引用,其又会对当前对象进行强引用,当前对象又会对其进行循环引用,而VC又会对当前对象进行循环引用,这时候就造成了一个环,导致内存泄漏。这时候如果将VC中创建的对象timer的关键字改为weak也无济于事,因为当VC释放对象的时候,timer并不会对系统中持有的定时器对象进行释放,是因为runloop对系统Timer对象还有一个强引用,导致系统中的Timer不会被正常释放,而系统Timer又对当前timer对象有一个强引用,这样就导致了当前对象timer无法被正常的释放了。遇到这种问题,大部分图省事的做法就是手动破环,当VC被回收的时候手动将timer调用废弃方法,回收系统对象。这种做法也可取,但是在VC非常多的时候,而且使用定时器的地方非常多的时候,稍不注意忘记回收timer就会导致内存的泄漏,而且这种方式也是非常耗费大量无用功的。所以我自己封装了一个weakTimer的自定义定时器。

将系统Timer实例对象,与VC中timer对象以及VC的关系变了一下,在中间加了一个过渡层对象。将过度对象弱引用VC为target,然后弱引用系统Timer,并且调用target中真正需要实现的方法,每次定时器的回调方法时,过度对象都会去判断这个target是否为nil,如果为nil就将定时器置空,如果对象还在,就会先判断目标方法是否相应,随后调用真正的方法。这种破环的模式即安全,又对调用者来说简单,且不易出错,比较提高效率。

 

 

本文章由作者原创,未经允许禁止转载!

你可能感兴趣的:(iOS循环引用梳理)