美文网首页
Block深入学习

Block深入学习

作者: 幸福相依 | 来源:发表于2018-03-28 20:48 被阅读0次


    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内部使用弱指针。为了避免使用过程中被回收,可以在内部转成强指针使用

    相关文章

      网友评论

          本文标题:Block深入学习

          本文链接:https://www.haomeiwen.com/subject/hwwucftx.html