“循环引用”

循环引用指两个对象相互强引用了对方,即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 弱引用,从而避免循环引用

你可能感兴趣的:(iOS-OC)