Block

作者: 我真的不是张亮 | 来源:发表于2018-08-15 23:07 被阅读6次

    1、什么是局部变量,什么是全局变量

        局部变量分为自动变量(就是我们平常在函数内部定义的变量,只是默认省略了auto修饰符)和静态变量(在函数内部定义,用static修饰),局部变量之所以称为局部变量,是因为出了作用域就会销毁,但是static修饰的局部变量会一直在内存中。

    2、Block的本质就是一个OC对象,他有isa指针,并且Block是封装了函数以及函数调用环境的OC对象   Block转化成的结构体中都拥有impl(里面包含了封装函数的地址)和desc(这里包括了这个Block的size 保留对象, copy方法,这个方法是用来将block从栈拷贝到堆中使用的,还有dispose方法,这个方法是block销毁的时候调用的,copy和dispose方法只有自动变量是对象类型的时候才会出现,因为只有对象类型才需要进行内存操作)

    3、为了保证Block能够访问外部的变量,Block有个外部变量捕获机制,自动变量和静态变量都会被Block捕获,但是自动变量是值捕获,就是值存自动变量的值,但是静态变量是指针捕获,因为金泰变量一直在内存中,所以可以直接调用就行,所以是指针捕获。捕获局部变量的原因是自动变量出了作用域就销毁了,可是Block出了函数作用域还可以在别的函数中调用,而你封装的Block内部又访问了你已经销毁的局部变量,所以Block内部必须把局部变量通过值捕获或者指针捕获保存在Block内部

    4、Block不会捕获全局变量,因为全局变量到处都能访问,跨函数也能访问,所以根本没必要再在Block内部保存一份了,直接访问就可以了

    Block捕获变量图  

    5、需要注意的是  如果Block内部访问了self,那么这个self也会被捕获,原因是self也是个局部变量,因为方法转换成C语言函数之后 都会变成XXX(self,_cmd),也就是说方法转换成C语言后都有固定的两个参数,一个是self,另一个是方法名称,作为参数,那么久肯定是局部变量,所以肯定会被捕获

    Block的类型

    1、Block都是继承自NSBlock类型的,Block分为__GlobalBlock__   、__ NSStackBlock__ 、__ NSMallocBlock__三种类型,其中__GlobalBlock__ 存放在数据区、__ NSStackBlock__存放在栈区、__ NSMallocBlock__存放在堆区

    2、只要你不访问自动变量,那么Block类型就是__GlobalBlock__ 如果你访问了自动变量,在MRC上你就会是__ NSStackBlock__类型,但是在ARC上,有下面4中情况,系统会自动把BLock copy一份到堆中

        2.1、使用copy方法

        2.2、赋值给strong指针

        2.3、block作为方法名字中含有UnsignBlock的方法参数(比如枚举边里数组)

        2.4、block作为作为GCD的方法参数   

    这4中情况下Block就是__ NSMallocBlock__,其余的 只要你没有访问自动变量,那就是__GlobalBlock__ ,访问了,那么你就是__ NSStackBlock__

    3、如果Block访问了自动变量,但是你的这个Block只是栈上面的Block,那么这个Block内部就不会堆自动变量进行引用,强弱引用都不会,因为Block都会随时不保,引用自动变量也没有多大意义。如果Block被拷贝到了堆上面,那么就会调用Block内部的Copy方法,copy法法内部就会调用_Block_object_assign函数,会根据自动变量是weak类型,还是strong类型还是unsafe_unretained类型,来对该自动变量进行强弱引用。当Block销毁的时候,会调用Block内部的dispose方法,根据_Block_object_dispose函数来对已经引用的自动变量进行释放

    Block修改自动变量

        1.block访问了自动变量,自动变量只是值传递,如果你要在block内部就改变不了这个自动变量的值

        2、但是如果你的自动变量是指针传递,那么因为你的内部引用了该指针,所以你就能改变这个指针所指对象的值(static修饰的局部变量,全局变量)

        3、如果不能修改自动变量,你需要用__block来修饰该自动变量,__block内部是将该自动变量包装成了一个新的对象,这个新的对象中有个指向自己的指针,如果你要修改该自动变量,他就会通过forwarding指针找到被包装的这个对象,然后修改里面的值

    Block修改被__block修饰的对象

    4、为什么非要弄一个forwarding指针来指向自己呢,为什么不通过Block内部指向结构体的指针找到你要修改的自动变量来进行修改呢

        答:因为自动变量生成的时候都是在栈区的,通过Block的自动捕获机制,还有用__block修饰之后,把你的这个自动变量包装成了一个结构体,Block内部的指向结构体的指针指向了这个结构体,Block一旦被copy到了堆区,那么这个结构体也被复制到了堆区,会调用block内部的copy函数,copy函数内部会调用_Block_object_assign函数,_Block_object_assign函数会对__block变量形成强引用(根据变量修饰符来决定),但是我们修改变量后,我们都希望只对堆取得自动变量进行修改,因为修改栈区的没意义,但是吐过你访问的是战区的自动变量,然后修改了,那就达不到效果了,这个时候,就需要指向自己的指针了,如果你访问的是站上的自动变量,站上的自动变量的forwarding指针指向的是堆中的__block包装的结构体,然后再堆中的结构体找到forwarding找到堆中结构体里面的自动变量进行修改,如果你修改的是堆中的自动变量,那就直接找到堆中的结构体进行修改,所以forwarding的作用就是无论你修改的是占中的自动变量还是堆中的,都能保证修改了堆中的自动变量

    forwarding指针

    5、Block在栈上时,并不会对__block变量进行强引用,当block被copy到堆时会调用block内部的copy函数,copy函数内部会调用_Block_object_assign函数,_Block_object_assign函数会对__block变量形成强引用(非对象类型,对象类型还要根据修饰符决定是否强引用,如果是MRC  那么一定是弱引用)

    6、当block从堆中移除时,会调用block内部的dispose函数,dispose函数内部会调用_Block_object_dispose函数,_Block_object_dispose函数会自动释放引用的__block变量(release)

    ARC循环引用问题图解 MRC下循环应用问题

    相关文章

      网友评论

          本文标题:Block

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