带你认识block

一.什么是block

Block是将函数及其执行上下文封装起来的对象。

如下:

通过 命令编译该.m 文件,发现该 block 被编译成这个形式:


其中 WYTest是文件名, blockTest是方法名,这些可以忽略。其中__WYTest__blockTest_block_impl_0结构体为


__block_impl 结构体为


block内部有 isa 指针,所以说其本质也是 OC 对象 block内部则为:


所以说Block是将函数及其执行上下文封装起来的对象既然Block内部封装了函数,那么它同样也有参数和返回值。

二、Block 变量截获

1、局部变量截获 是值截获。 比如:

这里的输出是 6 而不是 2,原因就是对局部变量 num 的截获是值截获。 同样,在 block 里如果修改变量 num,也是无效的,甚至编译器会报错。


打印为 1,2,3局部对象变量也是一样,截获的是值,而不是指针,在外部将其置为 nil,对 block 没有影响,而该对象调 用方法会影响.

2、局部静态变量截获 是指针截获。


输出为 2,意味着 num = 1 这里的修改 num 值是有效的,即是指针截获。 同样,在 block 里去修改变量 m,也是有效的。

3、全局变量,静态全局变量截获:不截获,直接取值。

我们同样用 clang 编译看下结果。

 static NSInteger num3 = 300;

  NSInteger num4 = 3000;

编译后


编译后

( impl.isa = &_NSConcreteStackBlock;这里注意到这一句,即说明该 block 是栈 block) 可以看到局部变量被编译成值形式,而静态变量被编成指针形式,全局变量并未截获。而__block 修饰的变 量也是以指针形式截获的,并且生成了一个新的结构体对象:


该对象有个属性:num5,即我们用 __block修饰的变量。这里__forwarding 是指向自身的(栈 block)。一般情况下,如果我们要对 block 截获的局部变量进行赋值操作需添加__block修饰符,而对全局变量,静态变量是不需要添加__block修饰符的。 另外,block 里访问 self 或成员变量都会去截获。

三、Block 的几种形式

分为全局Block(_NSConcreteGlobalBlock)、栈Block(_NSConcreteStackBlock)、堆Block(_NSConcreteMallocBlock)三种形式其中栈 Block 存储在栈(stack)区,堆 Block 存储在堆(heap)区,全局 Block 存储在已初始化数据(.data)区

1、不使用外部变量的 block 是全局 block

比如:


全局block

输出:


输出

2、使用外部变量并且未进行 copy 操作的 block 是栈 block

比如:


栈block

输出:


输出

3、对栈 block 进行 copy 操作,就是堆 block,而对全局 block 进行 copy,仍是全局 block

比如堆1中的全局进行copy操作,即赋值:


赋值

输出:


仍是全局block

而对2中的栈block进行赋值操作:

栈block进行赋值

输出:


成了堆block

对栈 blockcopy 之后,并不代表着栈 block 就消失了,左边的 mallock 是堆 block,右边被 copy 的仍是栈 block。比如:


输出:


输出

即如果对栈 进行copy,将会copy到堆区,对堆Block进行copy,将会增加引用计数,对全局Block 进行 copy,因为是已经初始化的,所以什么也不做。

另外,__block 变量在 时,由于__forwarding 的存在,栈上的 指针会指向堆上的 g 变量,而堆上的 指针指向其自身,所以,如果对 的修改,实际上是在修改堆上的 __block 变量。

即__forwarding 指针存在的意义就是,无论在任何内存位置,都可以顺利地访问同一个__block 变量。另外由于block捕获的 修饰的变量会去持有变量,那么如果用__block修饰self,且self持有Block,并且 block 内部使用到__block修饰的self时,就会造成多循环引用,即 self 持有 block,block 持有__block变量 ,__block变量持有self,造成内存泄漏。

如果要解决这种循环引用,可以主动断开__block变量对self的持有,即block内部使用完weakself后将其置为 nil,但这种方式有个问题,如果 一直不被调用,那么循环引用将一直存在。 所以,我们最好还是用__weak 来修饰.

你可能感兴趣的:(带你认识block)