一、你都遇到过哪些循环引用?你又是怎么解决的?
1、NSTimer
创建使用NSTimer
的时候,NSTimer
会默认对当前self有个强引用,所有在self使用打算完成的时候,一定要先使用NSTimer
的invalidate
来停止是否时间控制对self的引用。
[_timer invalidate];
2、Block
Block
也是比较常见的循环引用问题,在Block
中使用了self容易出现循环引用,因此很多人在使用block的时候,加入里面有用到self的操作都会声明一个__weak
来修饰self。其实便不是这样的,不是所有使用了Block
都会出现self循环引用问题,只有self拥有Block
的强引用才会出现这种情况。
所以一般在函数中临时使用Block
是不会出现循环应用的,因为这时候Block
引用是属于栈的。当栈上的Block
释放后,Block
中对self的引用计数也会减掉。
当然不一定要self对Block
有直接的引用才会出现,假如self的变量B,B中有个Block
变量,就容易出现这种情况,需要注意的是在Block
出现循环引用的,Xcode7之后的版本会出现警告提示(之前版本不确定)。
3、delegate
delegate
是iOS中开发中最常遇到的循环引用,一般在声明delegate
的时候都要使用弱引用weak
或者assign
@property (nonatomic, weak, nullable) id <UITableViewDelegate> delegate;
当然怎么选择使用assign
还是weak
,MRC的话只能用assign
,在ARC的情况下最好使用weak
,因为weak
修饰的变量在是否后自动为指向nil,防止不安全的野指针存在。
二、对Block基本的认识
本质上也是一个OC对象,它内部也有一个isa指针,是封装了函数调用以及函数调用环境的OC对象。
根据Block
的内存地址,系统把Block
分为3类:
NSGlobalBlock
:全局Block,位于内存全局区。内部没有引用局部变量
NSMallocBlock
:堆区Block,位于内存堆区。引用了局部变量,存放的位置是堆区
NSStackBlock
:栈区Block,位于内存栈区。其实上面的Block
在声明的时候其实默认隐藏了参数,默认参数是__strong
,对应的是__weak
,在ARC环境下,如果用__weak
修饰的Block
,就会生成栈区Block
(NSStackBlock
);当然,如果Block
内部没有引用外部变量,那么它还是全局的Block
。
由此可见,Block
是什么类型,主要是由两个因素决定的:
一个是是否引用了局部变量;
一个是是否是使用strong
修饰的。
三、为什么Block会产生循环引用?
Block
里面使用self不一定会引起循环引用。循环引用的原因是相互指引,相互是关键。如果相互这一层关系达不到就没有所谓的循环引用。
那么具体什么情况下会引起循环引用?
1、强引用自定义的Block
,里面再次调用self,导致的循环引用。
2、循环引用的发生的条件就是强持有这个Block
,并且被Block
里面的加入的对象强持有。
3、使用NSNotification使用系统自带的Block
会发生循环引用。
四、Block为什么要是用copy修饰符
因为Block
变量默认为栈变量,为了能够在Block
声明的作用域外使用,所以把Block
拷贝到堆上去。
简单来说,为了Block
属性声明和实际的操作一致,最好声明为copy
。
五、怎么理解Block截获变量的特性?
1、局部变量:基本数据类型,对象类型(对于基本数据类型的局部变量截获其值,对于对象类型的局部变量连同所有权修饰符一起截获)
2、静态局部变量:以指针形式截获局部静态变量
3、全局变量:不截获全局变量
4、静态全局变量:不截获静态全局变量