Block类型和捕获变量
Block类型
1:类型区别特征
Block是OC的对象,他也有类型,我在打印[block class]的时候可以看到有不同的block类型。
block 有三种类型,凡是访问了auto变量的便是__NSStaticBlock__类型,没有访问auto的是__NSGlobalBlock__,调用了copy的是__NSMallocBlock__。
他们的内存区域:
1:在全局区的__NSGlobalBlock__
2:在栈空间的__NSStaticBlock__
3:在堆空间的__NSMallocBlock__
这三个都继承于NSBlock,NSBlock继承于NSObect。
2:block的copy操作
在栈空间创建的block是__NSStaticBlock__,对它进行copy操作会把它变成__NSMallocBlock__类型,另外说明的是要在Objective-C Automatic Reference Counting 设置是否是ARC YES是ARC,需要调整成MRC模式才能看到类型转换。__NSMallocBlock__类型的block才能赋值给全局变量。如下
block = [^{NSLog("This block type is Malloc")} copy];
在ARC的环境下,一些情况,编译器会将block自动 copy搬到堆上去;
1:block作为返回值。
2:将block赋值给强指针__strong
3:block作为Cocoa API中方法名含有usingBlock的方法参数时。
4:block作为GCD API的方法参数时。
3:对象类型的auto变量
当我们的block对对象类型的变量捕获时,底层编译的代码__main_block_desc_0里会产生两个函数__main_block_copy_0和__main_block_dispose_0,block结构体里面也会多个成员接收被捕获的变量对象Person
在栈上的block 不会对auto类型的对象产生强引用。
如果block被拷贝到堆上,会调用内部的copy函数 ,copy函数内部会调用_Block_object_assign函数。_Block_object_assign函数会根据auto变量的修饰符(__strng,__weak, __unsafe_unretained)做出相应的操作。类似retain(强引用,弱引用)
如果block从堆上移除,会调用block内部的dispose函数。dispose函数内部会调用__Block_object_dispose函数 ,这个函数会自动释放引用的auto变量,类似release。
当block内部捕获了对象型auto变量时,block的核心结构体里会多个成员接收变量
__main_block_desc_0结构体里会多两个函数
捕获变量
变量分全局变量,和局部变量,其中局部变量分auto和static类型。
我们平常声明变量 比如:int a = 10;默认类型就是auto,离开了作用域就会销毁。
如果,在block内访问外部的变量,block会在内部声明成员接受外部的值,这叫捕获变量,auto是值捕获,在block里改变值不会改变作用域外面的值,static是指针捕获,在block内改变值会改变作用域外的值。全局变量不会被捕获,block可以直接调用。
图-01上图代码中age_,和height_是全局变量。上面那段代码,编译后的C++代码如下:
图-02__block 修饰符所干的事
我们在经常会在block修改外部变量,当我们修改外部的变量是static或者只是使用指针类型传递的变量时,不需要加__block,而要修改它的值就需要加__block.
__block到底做了什么,如下代码
在编译后的C++代码是:
由上面三幅图可以看出,当我们用__block修饰变量时,底层会对变量进行一个包装,把变量封在一个结构体里,如果被修饰的是对象,那么结构体里还会有copy和dispose函数,对对象进行retain和copy操作