关于Block用copy修饰的原因的一点自己的理解
先说结论:
ARC中在block作为属性时用copy修饰是MRC时代的遗留物,是一个习惯问题,提示程序猿这里可能有内存问题。在ARC中用copy保留这个习惯.
在ARC中,copy是可以用strong来代替的。(注意:这里只是说可以,并不推荐,接着往下看...)
block在内存中的存放位置
OC中的三种类型的block:
- 1,_NSConcreateGlobalBlock 全局的静态block,不会访问任何外部变量。
- 2,_NSConcreateStackBlock 保存在栈中的block,当函数返回时会被销毁。
- 3,_NSConcreateMallocBlock 保存在堆区的block,当引用计数为0时被销毁。
block块的存放位置可能在3个地方:
MRC下:
全局区(代码区)、堆区、栈区
ARC下:
全局区、堆区
ARC下栈区会自动拷贝到堆区,因此ARC下只有这两个地方
代码区类型block:不访问栈区以外的变量(如局部变量),也不访问堆区的变量(如alloc创建的对象),这种时候block放在代码区;
堆区类型的block:访问了外部变量,此时block存放在堆区;
注意
- 1,对于栈区block,在ARC情况下自动拷贝到堆区,MRC则放在栈区,所在函数执行完毕就会释放。那么要想在外面调用就要用copy指向它,这样
就copy到了堆区。 - 2,strong属性不会拷贝,会造成野指针错区从而crash。另外这里需要注意的是,ARC是编译器的一种特性,编译器在编译的时候会在合适的地方插入retain、release、autorelease,而不是iOS运行时的特性。
- 3,堆区是动态的,不像代码区是不变化的,堆区会不断地创建销毁,当没有强指针指向的时候就会被销毁,如果再去访问这段代码,程序就会崩溃。所以在堆区要用strong或者copy修饰。
- 4,block是一段代码块,即不可变,所以使用copy也不会深copy。
这就可以理解到block的定义:
block:可以理解为匿名的函数,就是预先准备好的一段代码。只不过这段代码里面包含的东西比较多,有isa指针、flags、invoke、descriptor、variables;
所以要用strong替代copy修饰block要满足两个条件:
用strong修饰且引用了外部变量
- 1,当ARC下block类型为strong并且引用了外部变量,编译时会自动进行copy(系统帮你进行),block会放在堆区
- 2,当ARC下block类型为strong并且没有引用外部变量,block会放在全局区,会变成全局类型。
另外:
一个block要使用self,会处理成在外部声明一个weak变量指向self,在block里又声明一个strong变量指向weakSelf?????
原因:block会把写在block里的变量copy一份,如果直接在block里使用self,(self对变量默认是强引用)self对block持有,block对self持有,导致循环引用,所以这里需要声明一个弱引用weakSelf,让block引用weakSelf,打破循环引用。
而这样会导致另外一个问题,因为weakSelf是对self的弱引用,如果这个时候控制器pop或者其他的方式引用计数为0,就会释放,如果这个block是异步调用而且调用的时候self已经释放了,这个时候weakSelf已就变成了nil。
当控制器(也可以是其他的控件)pop回来之后(或者一些其他的原因导致释放),网络请求完成,如果这个时候需要控制器做出反映,需要strongSelf再对weakSelf强引用一下。
但是,你可能会疑问,strongSelf对weakSelf强引用,weakSelf对self弱引用,最终不也是对self进行了强引用,会导致循环引用吗。不会的,因为strongSelf是在block里面声明的一个指针,当block执行完毕后,strongSelf会释放,这个时候将不再强引用weakSelf,所以self会正确的释放。