Block 带有自动变量和相关匿名函数的对象。
为什么出现Block,和block相比的就是函数,自带了执行上下文。函数内部内部,只能使用传入参数和全局变量。而Block的出现,对于Block内部使用的局部变量和对象(普通局部变量,复制值,使用__block 修饰,封装保存__block变量,局部静态变量,和对象需要保存指针 ,超出局部变量的作用域),编译器会自动将其封装到Block内部的结构体中,当Block使用的时候,就能使用这个超出了作用域的局部变量的值和对象。相当于编译器帮程序员实现了执行上下文封存。方便了操作。
注意:对于普通的对象方法,之所以能使用self这个关键字,来访问对象的成员变量和方法等,是因为对象方法编译成的函数,第一个参数就是当前对象。这和Block还是有区别的,Block能catch任意对象。先有runtime 后有Block
匿名函数:(没有名称的函数,只能通过函数指针进行调用)block 内部包含着一个匿名函数的函数指针,执行block就是执行这个匿名函数
自动变量:block内部会自动保存函数执行过程中需要的对象地址,或是值(保存自动变量的瞬间值)
Block 的格式,可以省略返回值,参数列表
可以将产生的BLock赋值给Block类型的变量,这个变量和基本的变量一致,可以作为局部变量,全局变量,函数参数,自动变量
截取自动变量值:在产生Block会截取局部变量的当前的瞬间值,之后就不会被局部变量的值变化影响,执行Block的时候还是会使用原来的值。Block内部使用的值是产生的时候的局部变量的瞬间值。
如果这里改变局部变量的值,会出现错误:
Variable is not assignable (missing __block type specifier)
只有使用__block 修饰之后,才能在block内部修改局部变量,在内部修改之后,局部变量的值也发生了修改
在block内部截获OC对象,并对OC对象进行赋值会发生错误。但是使用这个OC对象是没有问题,仍然能调用这个对象的方法。
Block 深入解析
编译后的代码:
源代码分析编译产生的CPP文件:__block_impl 这个结构体也有isa指针,说明Block也是一个对象
对于变量的截获:
一、对于局部基础变量,会直接截获这个基础变量的值,但是不能在这个Block修改这个局部变量,不然出现编译错误,因为catch的就是这个栈区局部变量的值,并没有这个栈区变量的地址,不能修改这个基础变量了。
1、是值传递,不是地址传递
2、至于编译失败,可能是不想让外界使用的时候出现歧义,这里的值和外面栈的局部变量已经没有关系了。
二、使用全局变量,局部静态变量
对于全局变量,不用进行截获,直接使用就行。对于局部静态变量,还是要截获,保存在block结构体中。这样就能使用局部静态变量超过作用域使用
传入局部静态变量的指针作为参数:
使用:
三、使用__block 进行修饰的局部变量
__block 修饰局部变量,代表这个局部变量会保存到Block结构体内部:
转换后的代码:
__block变量的结构体
使用__block 变量
当Block截获这个变量的时候:
__forwarding 指向当前的__block 变量
多个Block,可以截获同一个__block 变量,一个block修改了这个变量,那么后一个block使用的时候,就是修改后的值
Block的存储栈 堆 全局(程序的数据区域.data)
1、全局Block:在全局区创建的Block变量,因为不用catch任何局部变量,只使用全局变量,所以可以直接放到.data 区域(对于一些不是在全局区声明的Block,但是没有catch变量的Block,也会是GlobalBlock的类型)
2、_NSConcreteStackBlock 一般产生的都是栈区block
栈区的Block,超出作用域的时候,block回收,而对应的__block 变量也会回收
3、mallocBlock,在进行copy操作的时候,会将栈区的Block,copy到堆区,改变Block的isa指针
对于ARC情况下,会自动的将Block 从栈区拷贝到堆区:objc_retainBlock(非ARC下面,还是要自己进行拷贝的)
早期下面的代码就会出现问题,因为没有将StackBlock 拷贝到堆区,一旦栈区回收,再去通过Array调用Block,就会出现问题
但是现在已经没有问题,通过测试,向NSArray中存放Block对象,Block对象会被自动copy,Array会对里面的对象强引用,
弱指针:
强指针:
对于作为参数,和返回值的Block,作为参数传递,不会进行copy,但是作为返回值,会进行copy,也会使用objc_autoreleaseReturnValue,进行参数内存管理。
当copy Block时候对于他引用的__block 变量的影响:当copy Block的时候,会同时将栈区的__block 变量拷贝到堆区,使用__Block_object_assign
多个Block使用同一个__block变量,第一个block copy到堆区,__block 变量也进行复制了,第二个Block变量copy的时候,只是对__block 变量的引用计数+1
__forwording 对于__block 变量,复制到堆区,那么其栈区的__block 变量的__forwoding 指向的不再是栈区的__block 地址了,而是堆区的,所以才能保证两个变量的值同步,修改一个,另外一个也修改
Block内部截获OC对象,在Block结构体内部会强引用这个对象,保证这个对象不会因为超过作用域,而被回收:
这个对象的回收也是会被Block影响,当Block变量回收的时候,就会调用_Block_object_dispose方法,参数是这个对象,这个对象的引用计数也会减一
Block内部使用weak指针的,Block也不会进行强持有,会存在可能Block使用时对象已经回收的问题:
循环引用的问题:使用__weak 修饰self,Block内部使用弱指针。为了避免使用过程中被回收,可以在内部转成强指针使用
网友评论