美文网首页
iOS开发循环引用

iOS开发循环引用

作者: 清风_____ | 来源:发表于2021-03-31 16:11 被阅读0次

一、循环引用的产生

1.说明

-- :表示弱引用。

-> :表示强引用。

循环引用可以简单理解为对象A引用了对象B,而对象B又引用了对象A:A -> B -> A,此时双方都同时保持对方的一个引用,导致任何时候双方的引用计数都不为0,双方始终无法释放就造成内存泄漏。

当然不只是两个对象之间相互引用会形成循环引用,多个对象之间相互引用最终形成环同样会形成循环引用。

例如:A->B->C->....->X->B。

2.危害

循环引用对 app 有潜在的危害,会使内存消耗过高,导致内存泄漏,性能变差和 app 闪退等。

二、容易造成循环引用的三个场景

block 、 delegate 、NSTimer

1、delegate

self.tableView.delegate = self;
如果 delegate使用strong修饰就会构成循环引用:self -> tableView -> delegate -> self。

所以在定义delegate属性时使用weak便能解决这一问题:self -> tableView -- delegate -> self。tableView和delegate之间不是强引用,所以构不成循环。

规避delegate循环引用的杀手锏也是简单到哭:定义delegate属性时请用assign(MRC)或者weak(ARC),千万别手贱玩一下retain或者strong。

2.block

(1)并不是所有block都会产生循环引用,block是否产生循环引用是需要我们去判断的,例如

//这样是不会产生循环引用,因为这个block不被self持有,是被UIView的类对象持有,这个block和self没有任何关系,所以可以任意使用self。
[UIView animateWithDuration:0.0 animations:^{
    [self viewDidLoad];
}];

(2)self -> reachabilityManager -> block -> self,才会产生循环引用,并且XCode给出了循环引用warning,例如

//self -> reachabilityManager -> block -> self 都是循环引用
    self.reachabilityManager.stateBlock = ^(int number){
        NSLog(@"%@",self. reachabilityManager);
    };
//或者(block内部没有显式地出现"self",只要你在block里用到了self所拥有的东西,一样会出现循环引用!)
    self.reachabilityManager.stateBlock = ^(int number){
        NSLog(@"%@",_ reachabilityManager);
    };

(3)解决block循环引用的方法是使用__weak修饰self,然后在block里使用被修饰后的weakSelf来代替self:

__weak __typeof(self) weakSelf = self;
[self.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
    NSLog(@"%@",weakSelf.reachabilityManager);
}];
//但是仅仅使用__weak修饰self存在一个缺陷:__weak可能会导致内存提前回收weakSelf,在未执行NSLog()时,weakSelf就已经被释放了,然后执行NSLog()时就打印(null)。

//所以为了解决这个缺陷,我们需要这样在block内部再用__strong去修饰weakSelf:

__weak __typeof(self) weakSelf = self;
[self.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
    __strong __typeof(weakSelf) strongSelf = weakSelf;
    NSLog(@"%@",strongSelf.reachabilityManager);
}];

我们发现上述这个方法确实解决所有问题,但是可能会有两个不理解的点:
即使用weakSelf又使用strongSelf,这么做和直接用self有什么区别?为什么不会有循环引用?这是因为block外部的weakSelf是为了打破环循环引用,而block内部的strongSelf是为了防止weakSelf被提前释放,strongSelf仅仅是block中的局部变量,在block执行结束后被回收,不会再造成循环引用。
这么做和使用weakSelf有什么区别?唯一的区别就是多了一个strongSelf,而这里的strongSelf会使self的引用计数+1,使得self只有在block执行完,局部的strongSelf被回收后,self才会dealloc。
使用最终方法基本可以解决百分之九十九的block的循环引用问题。
解决 NSTimer 循环引用的方法

3. NSTimer

1、合适的时机启动和销毁 NSTimer
解决 NSTimer 的循环引用,我们首先会想到的方法应该是在 OneViewController dealloc 之前就销毁 NSTimer,这样循环就被打破了。
最简单的方法就是在 viewWillAppear 中启动 NSTimer,然后在 viewWillDisappear 中销毁 NSTimer,成对出现,绝对没有问题。

-(void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
     self.time = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(log) userInfo:nil repeats:YES];
}
 
- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [self.time invalidate];
    self.time = nil;
}

2、Effective Objective-C ”中的52条方法

#import <Foundation/Foundation.h>

@interface NSTimer (YPQBlocksSupport)

+ (NSTimer *)ypq_scheduledTimeWithTimeInterval:(NSTimeInterval)interval
                                         block:(void(^)())block
                                       repeats:(BOOL)repeats;

@end


#import "NSTimer+YPQBlocksSupport.h"

@implementation NSTimer (YPQBlocksSupport)


+ (NSTimer *)ypq_scheduledTimeWithTimeInterval:(NSTimeInterval)interval
                                         block:(void(^)())block
                                       repeats:(BOOL)repeats
{
    return [self scheduledTimerWithTimeInterval:interval
                                         target:self
                                       selector:@selector(ypq_blockInvoke:) userInfo:[block copy]
                                        repeats:repeats];
}

- (void)ypq_blockInvoke:(NSTimer *)timer
{
    void (^block)() = timer.userInfo;
    if(block)
    {
        block();
    }
}

@end
__weak ViewController * weakSelf = self;
[NSTimer ypq_scheduledTimeWithTimeInterval:4.0f
                                     block:^{
                                         ViewController * strongSelf = weakSelf;
                                         [strongSelf afterThreeSecondBeginAction];
                                     }
                                   repeats:YES];

计时器保留其目标对象,反复执行任务导致的循环,确实要注意,另外在dealloc的时候,不要忘了调用计时器中的 invalidate方法。

https://www.jianshu.com/p/41eb11ab4988
https://blog.csdn.net/qq_36557133/article/details/91355258
https://blog.csdn.net/qq_36557133/article/details/87654407
https://www.jianshu.com/p/ddfd1b3c0298

相关文章

  • Swift中Weak References弱引用和Unowned

    循环引用 循环引用在iOS开发中是需要非常重视的一个问题,不合理的循环引用会导致内存的泄漏,这在开发中是非常危险的...

  • NSTimer的循环引用问题解决方案

    iOS开发中,针对循环引用的问题,会有很多方面,block,代理,自循环,多循环,还有一个就是Timer的循环引用...

  • iOS开发循环引用

    一、循环引用的产生 1.说明 -- :表示弱引用。 -> :表示强引用。 循环引用可以简单理解为对象A引用了对象B...

  • iOS开发中的循环引用

    循环引用的实质是,多个对象之间相互强引用,导致不能释放,让系统回收。iOS开发中常见的循环引用主要是由Delega...

  • 一个奇怪且无聊的检测Block的想法

    在大多数iOS应用开发过程中, 循环引用一直都是最常见的iOS开发问题之一。通常情况下, 最常见的循环引用问题就是...

  • iOS闭包循环引用精讲

    iOS闭包循环引用精讲 iOS闭包循环引用精讲

  • weak-strong dance探究

    循环引用 循环引用是iOS开发常见的问题,虽然现在普遍是ARC工程,但是这个问题仍然无可避免。一般都是两个强引用对...

  • iOS开发循环引用理解

    在block中,并不是所有的block都会循造成环引用,比如UIView动画block、Masonry添加约束bl...

  • 如何在 iOS 中解决循环引用的问题

    如何在 iOS 中解决循环引用的问题 如何在 iOS 中解决循环引用的问题

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

    什么是循环引用 循环引用是iOS开发中经常遇到的问题,它指的是两个或多个对象通过相互之间的强引用,形成了一个保留环...

网友评论

      本文标题:iOS开发循环引用

      本文链接:https://www.haomeiwen.com/subject/ewzohltx.html