【OC梳理】循环引用及解决

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了【OC梳理】循环引用及解决相关的知识,希望对你有一定的参考价值。

参考技术A 循环引用是ios开发中经常遇到的问题,它指的是两个或多个对象通过相互之间的强引用,形成了一个保留环,即使已经没有外部对象持有,也无法对其进行释放操作,也无法释放其占用的内存空间(引用计数器始终大于0)。

举个简单的例子:
对象A持有对象B,对象B持有对象C,对象C持有对象A,这时候它们之间就形成了一个引用环:

这时候如果有一个对象D,引用了对象A,那么由于ABC之间的循环引用,它们的引用计数器如下:

那么即使D释放了对象A,A、B、C的引用计数器仍然都是1,它们都不会被释放回收。

由于循环引用的存在,使得产生循环引用的对象始终占有内存空间,过多的循环引用会导致程序的内存占用不断升高,最终导致程序Creach。

用weak而不是strong就能解决这个问题了:

Block的循环引用,主要是发生在ViewController中持有了block,比如:

同时在对callbackBlock进行赋值的时候又调用了ViewController的方法,比如:

就会发生循环引用,因为:ViewController->强引用了callback->强引用了ViewController,解决方法也很简单:

那是不是所有的block都会发生循环引用呢?其实不然,比如UIView的类方法Block动画,NSArray等的 类的遍历方法,都不会发生循环引用,因为当前控制器一般不会强引用一个类。

NSTimer是一种很容易忽略的循环引用的情况。
因为timer会强引用self,而self又持有了timer,这就造成了循环引用。
那么能不能像Block那样用一个weak指针解决呢?比如

但是其实并没有用,因为不管是weakSelf还是strongSelf,最终在NSTimer内部都会重新生成一个新的指针指向self,这是一个强引用的指针,结果就会导致循环引用。

如何解决呢?用NSProxy就是一个狠简便的方法

NSProxy本身是一个抽象类,它遵循NSObject协议,提供了消息转发的通用接口。NSProxy通常用来实现消息转发机制和惰性初始化资源。

使用NSProxy,你需要写一个子类继承它,然后需要实现init以及消息转发的相关方法:

创建NSTimer时,使用如下方法:

原理如下:

把虚线处变成了弱引用。于是,Controller就可以被释放掉,我们在Controller的dealloc中调用invalidate,就断掉了Runloop对Timer的引用,于是整个三个淡蓝色的就都被释放掉了。

参考文章: NSProxy与消息转发机制

Swift-闭包使用及解决循环引用问题

Swift中闭包使用参考OC中block使用,基本一致

//    闭包类型 首先写(参数列表)->(返回值类型)
    func loadData(callBack : (jsonData:String)->()){
        
        dispatch_async(dispatch_get_global_queue(0, 0)) { 
            print("网络请求\\(NSThread.currentThread())")
            
            dispatch_sync(dispatch_get_main_queue(), { 
                print("获取到数据\\(NSThread.currentThread())")
                callBack(jsonData: "数据")
            })
        }
    }

解决Swift中闭包循环引用有三种方法:

  1.跟oc一样,使用weak(oc是__weakSelf)

  2.简化第一种方法tools?.loadData({[weak self] (jsonData) in}) 这里self需要解包(推荐使用)

  3.简化第一种方法tools?.loadData({[unowned self] (jsonData) in})这种方法当self为空就会崩溃

 

以上是关于【OC梳理】循环引用及解决的主要内容,如果未能解决你的问题,请参考以下文章

Swift-闭包使用及解决循环引用问题

iOS之weak和strong懒加载及循环引用

iOS 中Block的理解以及啥时候会引起循环引用

Swift 内存管理与循环引用问题(weakunowned)

简单聊一聊JS中的循环引用及问题

OC中weak的原理