美文网首页
iOS 循环引用

iOS 循环引用

作者: CrystalZhu | 来源:发表于2020-02-19 17:36 被阅读0次

    产生原因: 两个强引用对象互相指向对方
    强引用对象互相指向时,双方的引用计数都+1,导致任何时候引用计数都不为0,始终无法释放,无法释放他们的内存,即使已经没有变量持有他们
    危害: 使内存消耗过高,性能变差,app闪退等.
    具体情况:

    1. 父类与子类
      例如在使用UITableView时,传一个Controller给cell使用,自定义 cell: TestTableViewCell,在cell里声明一个UITableView的变量,在cellForRowAtIndexPath里将tableView传入cell.
    -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexpath{
      TestTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellIdentifier" forIndexPath:indexPath];
      cell.tableView = tableView;
      return cell;
    }
    

    在声明tableView时,如果我们用strong来修饰的话就会造成循环引用

    @interface TestTableViewCell: UITableViewCell
    @property (nonatomic, weak) UITableView  *tableView;
    @end
    

    2.Block

    @property (nonatomic, copy) TestBlock completionHandler;
    self.completionHandler = ^{
      [self.property removeObserver: self forKeyPath: @"pathName"];
      // self do something
    }
    

    block在copy时都会对block内部用到的对象进行强引用,该类又将block作为自己属性变量,block的方法体里又使用了该类,形成了环状,造成了循环引用.
    避免:

    __weak typeof(self) weakself = self;
    self.completionHandler = ^{
      //和block外的self,指向一样,但是是不同的变量
      __strong typeof (weakself) strongSelf = weak self;
      [strongSelf doSomething];
    }
    

    先将强引用的对象转为弱引用指针,防止了block和对象之间循环引用,再在block里将弱指针转化为强引用指针,防止了多线程和ARC环境下弱引用随时被释放的问题.

    或者 改为Block传参:

    self.blk = ^ (UIViewController *vc){
      NSLog(@"use property: %@", vc.name);
    };
    self.blk(self);
    
    1. Delegate:
      我们在声明代理时,会写
    @property (nonatomic, weak) id <TestDelegate> delegate;
    

    如果我们不使用weak,而是使用了strong,那么两个类之间的代理就会变成:

    aViewController.delegate = self; //假设self为aViewController, a和delegate的引用计数均加1,a与delegate相互强指向,造成循环引用.
    

    4.NSTimer
    ViewController --强引用--> 属性Timer --强引用--> Target(self = ViewController) --强引用--> ViewController

    比如在ViewController B 中有一个Timer

    @property (nonatomic, strong) NSTimer *timer;
    

    创建并挂载到main runloop

    self.timer = [NSTimer scheduledTimerWithTimerInterval: 1 target: self selector:@selector(timerAction:) userInfo:nil repeats: true];
    

    然后退出Controller B的时候忘记关掉timer, Controller B将不会释放, B与timer循环引用,因为创建timer时指向了self
    避免:
    在适当的情况下,invalidate timer,因为timer在validate状态下引用计数始终大于0
    注意:在上面的方法中,repeats参数为YSE时,NSTimer会保留目标对象,等到自身失效才释放.执行完毕任务后,一次性的定时器会自动失效,重复性的定时器,需要手动调用invalidate方法才会失效

    若单纯的想在 Controller B 的dealloc方法中去释放timer是不可能调用的,因为timer没有释放的话,controller B也是不能释放的,dealloc方法不会被调用

    相关文章

      网友评论

          本文标题:iOS 循环引用

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