“循环引用”

循环引用指两个对象相互强引用了对方,即retain了对方,从而导致两个对象都无法被释放,引发了内存泄漏现象。

在开发中很容易出现循环引用,循环引用可能存在于代码的每个角落,会使内存消耗过高,性能变差,也可能会导致程序崩溃。

ARC的缺点:
  1. 没有设计一个循环引用检测器,容易出现循环引用导致的内存泄漏
  2. 和非ARC代码混用问题。有时候添加-fno-objc-arc还会出现问题,一旦你的程序中有ARC和非ARC这两个class之间交互的时候,就会出现内存泄露!只能重写其中一个class以保证两者均为ARC。
  3. 对于内存性能要求很高的项目,ARC做的优化不够好

容易引起循环引用的情况:

  1. Bolck(Swift中是闭包)
  2. 代理
  3. 定时器
  4. 父子对象关系
1. Block / 闭包

举例:控制器创建了一个属性,这个属性是个闭包,在使用闭包的时候,在闭包中写了self.view等等,就会出现控制器强引用了闭包,闭包强引用了控制器,形成循环引用,在退出控制器的时候,控制器不会被释放。

如果在block中进行了一些调用,那么将会有一个强引用指向这些调用方法的调用者(block需要拷贝到堆上,在拷贝的时候会retain其引用的外部变量),如果是通过引用来访问一个实例变量,那么将强引用至self,如果是通过值来访问一个实例变量,那么将直接强引用至这个“值”变量。

block中避免强引用到self的方法:

  • block里面引用了self导致循环引用
//在block的外部前面加上这样一行代码
__weak ViewController *weakSelf = self;
  • block中没有显示的使用self导致的循环引用(即使在你的block/闭包代码中没有显式地出现"self",也会出现循环引用!只要你在block里用到了self所拥有的东西!
__weak typeof(self) weakSelf = self;
self.blkA = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;//加一下强引用,避免weakSelf被释放掉
NSLog(@"%@", strongSelf->_xxView); //不会导致循环引用.
};

闭包中也可是使用上面的方法,但还有一个更简单的方法:

var myClosureVar = {
    //只需要在必报的头部声明 [weak self] in
    [weak self] in
    self?.doSomething()
}

和我们通常所认为的不同,在使用GCD的时候,dispatch_async自身不会造成循环引用,并不是所有的block 和闭包里面的 self 都需要弱引用

2. 代理

声明一个代理时一般使用assign而不是用retain或strong,后者很容易导致循环引用。

例如:如果父VC持有子VC,并设置子VC的delegate为self(也就是父VC),这样的结果就是子VC也间接持有了父VC,造成循环引用,

至于为什么不使用week,在网上查了查,有说“如果想在dellac中调用delegate的某些函数时候,如果是weak会找不到对象,因为被置空了。所以用assign”,不是太理解,只知道weak比assign多了一个功能,当对象消失后自动把指针变成nil

3. 定时器

timer是否持有self,我们一般要执行一个timer的时候会用(NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo 这里的aTarget一般是self,这时候就需要注意了,如果在你退出的时候这个timer还在执行的话由于这个timer会持有self,所以delloc也不会调用,这里可以用weakSelf代替self也是没有问题的。

4. 父子对象关系

在子类定义一个指向父类的变量,声明为 weak 弱引用,从而避免循环引用

<think>我们正在解决C++中shared_ptr循环引用的问题。根据引用[2]和引用[3],循环引用发生在两个或多个对象互相使用shared_ptr指向对方,导致引用计数无法归零,从而内存泄漏。 解决方法: 1. 使用weak_ptr打破循环引用。weak_ptr是一种弱引用,不会增加引用计数。在循环引用的场景中,将其中一个指针改为weak_ptr即可。 2. 在需要访问weak_ptr指向的对象时,使用lock()方法将其转换为shared_ptr,以确保对象存在。 引用[3]和引用[4]给出了具体示例: - 在链表中,将_next和_prev改为weak_ptr类型。 - 使用lock()来获取shared_ptr,然后使用。 下面我们用一个示例来说明如何解决循环引用问题。 原始循环引用示例(引用[2]): class A和class B互相持有对方的shared_ptr,导致析构函数不被调用。 修改后的解决方案(引用[3]和引用[4]): 将其中一个指针改为weak_ptr,例如将class A中的ptr_to_b改为weak_ptr,或者将class B中的ptr_to_a改为weak_ptr。 我们修改引用[2]的例子: 将A中的ptr_to_b改为weak_ptr,这样当main函数中的a和b离开作用域时,a的引用计数变为1(因为b的ptr_to_a是shared_ptr,但b的引用计数变为1,因为a中的ptr_to_b是weak_ptr,不增加引用计数),然后b的引用计数变为0,先销毁b,接着b中的ptr_to_a(指向a)被销毁,导致a的引用计数变为0,从而销毁a。 注意:weak_ptr需要通过lock()来获取shared_ptr,然后才能访问对象。在lock()之后,我们需要检查返回的shared_ptr是否为空(即对象是否已被销毁)。 修改后的代码示例:</think>### C++ shared_ptr 循环引用解决方法 在 C++ 中使用 `shared_ptr` 时,**循环引用**会导致内存泄漏,因为相互持有的 `shared_ptr` 会阻止引用计数归零,从而无法释放对象。以下是详细解决方案: #### 🔍 问题本质 - 循环引用场景:对象 A 持有对象 B 的 `shared_ptr`,同时对象 B 持有对象 A 的 `shared_ptr` - 引用计数永不归零:$ \text{ref\_count}(A) \geq 1 $ 且 $ \text{ref\_count}(B) \geq 1 $,导致析构函数不被调用[^2] #### 💡 解决方案:使用 `weak_ptr` `weak_ptr` 是专门为解决循环引用设计的弱指针,其特性: 1. **不增加引用计数**:$ \text{weak\_ptr} \nRightarrow \text{ref\_count} + 1 $ 2. **不拥有对象所有权** 3. **可安全检测对象是否存在** ##### 修改步骤: 1. 将循环引用中的某个 `shared_ptr` 替换为 `weak_ptr` ```cpp class B; // 前向声明 class A { public: std::weak_ptr<B> ptr_to_b; // 关键修改 ~A() { std::cout << "A destroyed\n"; } }; class B { public: std::shared_ptr<A> ptr_to_a; ~B() { std::cout << "B destroyed\n"; } }; ``` 2. 访问对象时使用 `lock()` 方法: ```cpp void use_object(A& a) { if (auto b_ptr = a.ptr_to_b.lock()) { // 转换为 shared_ptr // 安全使用 b_ptr } else { // 对象已被释放 } } ``` #### ⚠️ 使用注意事项 1. **检查有效性**: ```cpp if (!weak_ptr.expired()) { // 检查对象是否存活 auto obj = weak_ptr.lock(); // 获取 shared_ptr } ``` 2. **避免直接访问**:`weak_ptr` 不能直接解引用,必须通过 `lock()` 获取 `shared_ptr`[^4] 3. **生命周期管理**:`weak_ptr` 不延长对象生命周期,对象可能在检查后立即被销毁 #### 🌰 链表应用示例(引用[3]) ```cpp struct ListNode { int val; std::weak_ptr<ListNode> prev; // 弱指针打破循环 std::weak_ptr<ListNode> next; // 弱指针打破循环 ~ListNode() { std::cout << "Node destroyed\n"; } }; int main() { auto n1 = std::make_shared<ListNode>(1); auto n2 = std::make_shared<ListNode>(2); n1->next = n2; // weak_ptr 赋值 n2->prev = n1; // weak_ptr 赋值 // 访问节点示例 if (auto next = n1->next.lock()) { std::cout << "Next node value: " << next->val << "\n"; } return 0; } ``` #### 📌 关键结论 1. `weak_ptr` 通过不增加引用计数 $ \text{(ref\_count} \not\rightarrow \infty) $ 打破循环依赖[^3] 2. 使用 `lock()` 获取临时 `shared_ptr` 是线程安全的 3. 适用于:双向链表、父子对象、观察者模式等存在双向持有的场景 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值