关于block在内存中的位置:
OC中,一般Block就分为以下3种,_NSConcreteStackBlock,_NSConcreteMallocBlock,_NSConcreteGlobalBlock。
先来说明一下3者的区别。
1.从捕获外部变量的角度上来看(注意:MRC下和ARC下_NSConcreteStackBlock和_NSConcreteMallocBlock不一样,ARC下绝大部分_NSConcreteStackBlock类型的block被转换成了_NSConcreteMallocBlock)
-
_NSConcreteStackBlock:
只用到外部局部变量、成员属性变量,且没有强指针引用的block都是StackBlock。
StackBlock的生命周期由系统控制的,一旦返回之后,就被系统销毁了。 -
_NSConcreteMallocBlock:
有强指针引用或copy修饰的成员属性引用的block会被复制一份到堆中成为MallocBlock,没有强指针引用即销毁,生命周期由程序员控制 -
_NSConcreteGlobalBlock:
没有用到外界变量或只用到全局变量、静态变量的block为_NSConcreteGlobalBlock,生命周期从创建到应用程序结束。
ARC环境下,编译器会自动的判断,把Block自动的从栈copy到堆。比如当Block作为函数返回值的时候,肯定会copy到堆上。
1.手动调用copy
2.Block是函数的返回值
3.Block被强引用,Block被赋值给__strong或者id类型
4.调用系统API入参中含有usingBlcok的方法
以上4种情况,系统都会默认调用copy方法把Block赋复制。
这里再说一下,我们平时使用@weakify和@strongify的原理:
@weakify(self);
self.block = ^(){
@strongify(self);
...
};
上边的代码,等价于:
__weak typeof(self)weakSelf = self;
self.block = ^(){
__strong typeof(weakSelf)strongSelf = weakSelf;
...
};
@weakify和@strongify就是通过一系列的宏定义,最终会转换成类似图二的代码。
@weakify作用:将当前对象声明为weak类型,这样block内部引用当前对象,就不会造成引用计数+1,可以破解循环引用
@strongify作用:相当于声明一个局部的strong对象,指向当前对象。可以保证调用block的时候,在block整个执行过程中内部的self对象不会释放。因为是局部变量,所以,在整个block执行完之前是不会被释放的,而当block执行完之后会自动释放,这样也防止了总是持有self对象而引发的循环引用。
总结
- block在C语言层面就是结构体,结构体存储了函数指针和捕获的变量列表
- block分为全局,栈上,堆上三种,ARC开启的时候,会自动把栈上的block拷贝到堆上
__block
变量在C语言层面也是一个结构体- block捕获在堆中创建的对象的时候会增加对象的引用计数。
- Block不允许修改外部变量的值,这里所说的外部变量,指的是在栈中分配的变量。
参考文章: