`

OC 循环引用

 
阅读更多

简单一点的说是:

     比如在main函数中创建了两个类的对象A和B,现在引用计数都是1。现在让A和B互相引用,现在两个对象的引用计数都增加了1,都变成了2。
      现在执行 [A release]; [B  release]; 此时创建对象的main函数已经释放了自己对对象的所有权,但是此时A和B的引用计数都还是1.因为他们互相引用了。
      这时你发现A和B将无法释放,因为要想释放A必须先释放B,在B的dealloc方法中再释放A。同理,要想释放B必须先释放A,在A的dealloc方法中再释放B。所以这两个对象将一直存在在内存中而不释放。这就是所谓的循环引用的问题。
      要想解决这个问题,一般的方法可以将引用的属性设置为assign,而不是retain来处理。

 

首先说下什么是循环引用,循环引用是指两个对象互相retain对方,通过OBJC的release是无法销毁这两个对象的。更严重的是,如果几个对象间接相互引用,比如A<-B,B<-C,C<-A,那么A、B、C都无法通过release释放。
例如下面的引用关系:
• 对象a创建并引用到了对象b.
• 对象b创建并引用到了对象c.
• 对象c创建并引用到了对象b.
这时候b和c的引用计数分别是2和1。当a不再使用b,调用release释放对b的所有权,因为c还引用了b,所以b的引用计数为1,b不会被释放。b不释放,c的引用计数就是1,c也不会被释放。从此,b和c永远留在内存中。
这 种情况,必须打断循环引用,通过其他规则来维护引用关系。

引用链条中只要有一环发生循环引用,就会导致内存泄露。工具难以查出,因为符合使用约定。

遗憾的是相互引用十分常见,对于界面来说有时是必要的,特别是OBJC大量使用委托模式进行回调。因此OBJC也建议委托最好不进行retain,而改用 assign,以避免循环引用。不过一些异步调用,多线程使用是很难通过此种方式解决的,容易出现野指针。所以要解决循环引用导致的内存泄露需要从根源理 解泄露出现的原因,而不是单纯禁止使用。

循环引用问题,是引用计数所引入的问题。因此使用引用计数作为数据管理的架构都会出现类似问题。JAVA、C#的垃圾回收机制都有专门的算法来解决此问 题。引数管理的数据都是共享关系,相互引用就是相互共享,这种内存泄露是符合共享逻辑的。但实际情况中,当出现相互引用,往往存在依赖关系。一个引用为拥 有,而另一个仅为回调。回调引用依赖拥有引用。引数管理不能直接描述依赖关系,因此需要手工打断。

现假设B处由于某种原因使C被释放掉,比如线程结束或是Cancel操作,则此时在由root处释放A时,则全部释放。

所以不是只要循环引用就会内存泄露,通过打断解决内存泄露是有效的,因为打断补充了相互引用的依赖逻辑。但要注意,如果出于省事仅将打断代码放置在dealloc中,则仍然会出现内存泄露。原 因在于release字面意思是释放的意思,但这里的释放不是内存的最终释放而是所有权的释放。调用release是分不清内存释放还是仅仅减了一个计 数。循环引用时,存在于dealloc中的打断代码,将永远不会通过release调用。因此要解决循环引用,还要注意不要将打断代码放置在 dealloc中,而要在使用完毕后立即打断,dealloc的打断只能作为象征性防护。

总结:

防止循环引用导致的内存泄露,基本上有两种方法。

1)如果其中一个对象的生命周期覆盖另一个对象,则采用OBJC推荐的策略,短周期对象保持长周期对象使用assign。

2)如果相互引用的对象其生命周期不能覆盖对方,则必须在各自生命周期的终点处打断对方,打断代码不能依赖在dealloc时打断。比如对于UI对象可通过事件处打断,线程对象于Main结尾处打断。只要有一处打断被执行就不会产生内存泄露。

此外还有另一种解决方法——利用指针的指针建立依赖引用机制。

这里提供一下思路:引用双方只assign不retain,双方再额外存储对方引用的指针,当一方release时顺便将自身在对方的引用清空,以达到通知连接断开的目的。可将这种引用关系封装为一个类简化使用。

优点:避免循环引用导致内存泄露,即便在release处释放连接也不会引起内存泄露。

缺点:引用返回的是id类型,因此使用时需要类型转换。

另外编程时还要注意非预期的循环引用。比如OBJC库中经常使用字典作为参数的保存位置,而字典使用retain保存对象。如果A保持字典,字典中又保持A,或者保持B,但B保持A,则有可能产生内存泄露。

总之循环引用引起内存泄露,是需要在OBJC中额外注意的。C/C++不会产生此问题,因为引用需要手工维护。JAVA、C#中由于垃圾回收机制,也无需刻意注意。

分享到:
评论

相关推荐

    Block循环引用的问题

    在iOS开发中,Objective-C...了解并掌握如何处理Block循环引用是每个OC开发者必备的技能,这不仅能优化应用性能,还能减少因内存泄漏导致的程序崩溃。通过不断学习和实践,我们可以更好地应对这类问题,提升代码质量。

    Objective-C中block循环引用问题详解

    然而,这种便利性有时也会带来一个问题,那就是循环引用(Cyclic Reference)。循环引用通常发生在对象A持有一个Block,而这个Block内部又引用了对象A的情况。这种情况下,对象A和Block之间形成了一个无法自动解除的...

    OC中的内存管理

    在OC中,循环引用是一种常见的问题,无论使用MRC还是ARC,都可能导致对象无法被正确释放。循环引用发生在一个对象强引用另一个对象,而另一个对象又强引用第一个对象的情况下。为了解决这个问题,OC引入了弱引用(`_...

    Swift中如何避免循环引用的方法

    Swift中如何避免循环引用的方法 在 Swift 中,内存管理是一个非常重要的课题。自动引用计数(ARC)是 Swift 中的一种内存管理机制,它可以自动释放不再需要的内存空间。然而,在实际开发中,仍然会出现循环引用的...

    iOS面试中如何优雅回答Block导致循环引用的问题

    在iOS面试中,面对关于Block导致循环引用的问题,你需要展现出深度理解和实践经验,而不仅仅是复述常见解决方案。首先,理解循环引用的基本概念至关重要:当两个对象互相强引用时,导致它们都不能被正确释放,这就...

    oc基础教程全集

    - **强引用(strong)与弱引用(weak)**:理解这两种引用类型对于避免内存循环引用至关重要。 ### 第八章:块(Block) - **块**:OC中的匿名函数,常用于回调和并发编程。 ### 第九章:集合类 - **NSArray, ...

    iOS中wkwebView内存泄漏与循环引用问题详解

    然而,使用WKWebView时,开发者可能会遇到内存泄漏和循环引用的问题,这些问题如果不妥善处理,可能会导致应用程序性能下降,甚至引发应用程序崩溃。 首先,我们需要理解内存泄漏的概念。内存泄漏是指程序中已分配...

    Oc调用 swift

    在进行类型转换时,需要注意避免循环引用和内存泄漏。 **6. 错误处理** Swift使用`do-catch`语句处理错误,而Objective-C使用异常。为了在两种语言之间传递错误,通常需要定义一个共享的错误域和错误代码。 总结...

    OC底层原理之OC语法

    Block的内存管理与普通对象类似,分为栈Block、堆Block和强引用循环。Block内部的变量捕获方式有弱引用和强引用两种,会影响Block的生命周期和拷贝行为。 以上知识点涵盖了Objective-C的基础和高级特性,深入理解...

    详解iOS 用于解决循环引用的block timer

    一、什么是回调函数? 回调函数,本质上也是个函数(搁置函数和方法的争议,就当这二者是一回事)。由“声明”、“实现”、“调用”三部分组成。 在上面的例子中,我可以看出,函数amount(其实是Block),的声明和...

    OC与JS交互

    - **循环引用**:使用Block进行回调时需要注意避免循环引用问题。 - **子线程**:Block回调可能会在非主线程中执行,需要注意UI操作。 #### 六、其他框架和技术 ##### 6.1 WebViewJavascriptBridge - **设置桥接*...

    OC植物大战僵尸

    5. **游戏循环**:游戏通常有一个主循环,负责不断更新游戏状态、渲染画面和处理用户输入。在OC中,这可能通过CADisplayLink或NSTimer来实现。 6. **动画和特效**:Objective-C支持Core Animation框架,可以用来...

    OC 中的内存管理

    - **使用弱引用来避免循环引用**(Use Weak References to Avoid Retain Cycles):通过使用弱引用(weak reference),可以有效地避免因为循环引用而造成的内存泄露问题。 - **避免导致正在使用的对象被释放**...

    iOS 学习OC语言部分,代码和笔记,超详细

    6. Memory Management:OC使用ARC(Automatic Reference Counting)进行内存管理,理解引用计数和循环引用是避免内存泄漏的关键。 总结,这个压缩包包含的内容可以帮助初学者系统地学习Objective-C语言,了解其在...

    OC中以block形式页面传值

    - 使用Block时需要注意循环引用问题,特别是当Block内部引用了强指针self时。为避免循环引用,可以使用弱引用`__weak typeof(self) weakSelf = self;`并在Block内部使用weakSelf。 6. **扩展:Block与Delegate的...

    iOS MRC 下 block 循环引用问题实例讲解

    下面一段代码给大家介绍iOS MRC 下 block 循环引用问题  //注意此__block会复制一份指针出来 一次原始的指针如果置为nil的话,此处复制出来的指针还是野指针 __block __typeof(self)weakSelf = self; //__weak _...

    OC-GCD异步线程下载图片

    // 创建一个强引用,防止Block捕获self导致循环引用 __weak typeof(self) weakSelf = self; // 将下载任务放入队列 dispatch_async(downloadQueue, ^{ // 实际的下载操作,比如使用NSURLSession UIImage *...

    OC写的寻路算法

    1. **节点(Node)**:每个节点代表地图上的一个位置,包含位置坐标、代价(g-score)、启发式信息(h-score)以及父节点引用,用于回溯找到路径。 2. **优先队列(Priority Queue)**:使用NSPriorityQueue或...

    Swift_Demo oc转swift

    Swift采用了自动引用计数(ARC),并且引入了强引用(strong)、弱引用(weak)和无主引用(unowned)来防止引用循环,这是OC中强引用导致的内存泄漏问题的有效解决方案。 此外,Swift还引入了结构体(Struct)和...

Global site tag (gtag.js) - Google Analytics