美文网首页
block详解

block详解

作者: 嘿_原来你也在这里 | 来源:发表于2021-07-16 07:36 被阅读0次

    block本质:

    每个OC对象内部都有isa指针,block也不例外;block其实就是封装函数调用,以及函数调用环境的OC对象。

    block底层结构:

    block底层结构

    block的类型:

    内存几大区域

    常见的有以下三种block:

    NSMallocBlock :存放在堆区的 Block

    NSStackBlock  : 存放在栈区的 Block

    NSGlobalBlock : 存放在全局区的 Block

    当block 没有截获外部变量、截获全局变量的都是属于全局区的 Block,即 GlobalBlock;其余的都是栈区的 Block,即 StackBlock;

    对于全局区的 Block,是不存在作用域的问题,但是栈区 Block 不同,在作用域结束后就会 pop 出栈,__block 变量也是在栈区的,同理作用域结束也会 pop 出栈。

    为了解决作用域的问题,block 提供了 copy 函数,将 block 从栈复制到堆上,在 MRC 环境下需要我们自己调用 block_copy  函数,这里就是为什么 MRC 下,我们为什么需要用 copy 来修饰 Block 的原因。

    MRC情况下:

    MRC的情况下

    ARC情况下:

    编译器会根据情况自动添加copy,将栈上的block拷贝到堆上,比如以下情况:

    1、block作为函数返回值

    2、block赋值给__strong指针

    3、block作为Cocoa API中的方法名含有usingBlock的方法参数时

    4、block作为GCD API参数时

    每一种类型的block调用copy后的结果如下:

    使用copy结果示意图

    block捕获机制:

    block捕获机制 捕获auto、static局部变量

    因为auto的局部变量在离开作用域会销毁,所以block内部使用值传递的方式捕获到,因此在block内部不能更改auto变量的值,像int因为值是直接存储在变量内存里面,所以不能更改;而static因为是指针传递所有能更改。

    更改mArr内容 更改mArr指针地址

    因为NSMutableArray 变量里面其实存的是指针地址,所以直接操作指针里面的内容是可以的,如果直接更改变量存储的指针地址就不行。

    如果是全局变量不会捕获,不管在哪个函数都能直接访问;局部变量因为作用域的问题,可能存在跨函数访问所以会捕获。

    如果block中引用实例变量(比如说name)不管是通过self.name还是_name,block都会捕获self对象.因为方法的调用默认传递两个参数(self,sel)所以self为局部变量,block引用局部变量都会捕获。

    既然block对局部auto变量是值传递,那么如果想要在block内部修改auto变量怎么修改?

    如果block外部变量是auto变量可以使用__block;如果是static静态变量 或者全局变量,则不需要修饰也能更改。(参考捕获机制)

    原理:在ARC环境下,编译器会尽可能给我们自动添加 copy 操作。使用copy从栈复制到堆上,__block 修饰的变量也会从栈复制到堆上;为了结构体 __block 变量无论在栈上还是在堆上,都可以正确的访问变量,我们需要 forwarding 指针forwarding指针的作用:保证当我们将 Block 从栈拷贝到堆中,修改的变量都是同一份。

    forwarding原理:在 Block 从栈复制到堆上的时候,原本栈上结构体的 forwarding 指针,会改变指向,直接指向堆上的结构体。这样子就可以保证之后我们都是访问同一个结构体中的变量,这里就是为什么 __block 修饰的变量,在 Block 内部中可以修改的原因了。

    forwarding指针

    __block底层实现:

    当__block变量在栈上时:

    并不会对指向的对象产生强引用。

    当__block变量被copy到堆上时:

    copy函数内会调用_Block_object_assign函数;_Block_object_assign函数会根据auto变量(对象类型)的修饰符(__strong,__weak, __unsafe_unretained)做出相应的操作形成强、弱引用;

    当__block变量被从堆上移除时:

    会调用Block内部的dispose函数 dispose函数内部会调用_Block_object_dispose函数 _Block_object_dispose函数会自动释放引用的auto变量,类似于release。

    block引起循环引用场景:

    当 A 持有 B,B 又持有 A,这个时候就会出现循环引用。Block 对于外部变量都会追加到结构体中,所以在实现 Block 时候需要注意这个问题。

    解决方案:

    ARC 环境一般我们用 __weak 来打破,MRC 环境的话,我们可以使用 __block 来打破循环引用。

    相关文章

      网友评论

          本文标题:block详解

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