Block
Block介绍
Block是将函数及其执行上下文封装起来的对象
什么是Block调用
本质是函数调用
截获变量
-
局部变量
- 基本数据类型 -> 截获其值
- 对象类型 -> 连同对象所有权修饰符一起截获
-
静态局部变量 -> 以指针形式截获
-
全局变量 -> 不截获
-
静态全局变量 -> 不截获
__block修饰符
一般情况下,对被截获变量进行赋值操作需添加__block操作符
局部变量(无论是基本数据类型还是对象)需要赋值的时候要用到__block
静态局部变量、全局变量和静态全局变量就不需要
__block修饰的变量变成了对象
栈上的block的__forwarding指针是指向本身的
Block的内存管理
block的分类
-
全局Block - Global .data区
- copy - 什么都不做
-
栈上Block - Stack
- 增加引用计算
-
堆上Block Malloc
- copy到堆上
__forwarding的总结
-
block只在栈上
- __forwarding指针指向栈上block本身
-
block被Copy到堆上
- 栈上的__forwarding指针指向堆上block的copy
- 堆上的__forwarding指针指向堆上block本身
不论在任何内存,都可以顺利的访问一个__block变量
Block的循环引用
__weak可以解除自循环引用
block内部使用__block
-
在MRC下,不会产生循环引用
-
在ARC下,会产生循环引用,会引起内存泄漏,是大环引用
- 对象持有block,block持有block变量, __ block持有对象,就会变成大环引用,无法释放
面试总结
什么是block?
Block是将函数及其执行上下文封装起来的对象
为什么Block会产生循环引用?
- 连同对象所有权修饰符一起截获,如果self对对象也是强引用,那就会出现自循环引用
- __block也会导致循环引用
怎么理解Block截获变量的特性?
-
局部变量
- 基本数据类型 -> 截获其值
- 对象类型 -> 连同对象所有权修饰符一起截获
-
静态局部变量 -> 以指针形式截获
-
全局变量 -> 不截获
-
静态全局变量 -> 不截获
Block的类型?
- 初始化刚刚结束的时候,block会被放到数据区。
- 在block内部访问外界变量,block会被转移到栈空间存储。
- 在ARC环境下,访问外界变量的block会先被转移到栈空间,然后copy到堆空间。
Block复制拷贝到堆的时机
- 为什么要复制,由于Block是默认建立在栈上, 所以如果离开方法作用域, Block就会被丢弃
- 调用block的copy函数时;
- Block作为函数返回值返回时;
- 将Block赋值给附有__strong修饰符id类型的类或者Block类型成员变量时;
- 方法中含有usingBlock的Cocoa框架方法或者GCD的API中传递Block时;
- 这些情况下,可归结为
_Block_copy
函数被调用时Block
从栈复制到堆。释放Block
时调用其dispose
函数,相当于对象的dealloc
实例方法。
在 ARC 中,捕获外部了变量的 block 的类会是 NSMallocBlock 或者 NSStackBlock,如果 block 被赋值给了某个变量在这个过程中会执行 _Block_copy 将原有的 NSStackBlock 变成 NSMallocBlock;
但是如果 block 没有被赋值给某个变量,那它的类型就是 NSStackBlock;
没有捕获外部变量的 block 的类会是 NSGlobalBlock 即不在堆上,也不在栈上,它类似 C 语言函数一样会在代码段中。