OC对象 - Block的类型
1. Block有三种类型
- 全局:
__NSGlobalBlock__
( _NSConcreteGlobalBlock ) - 栈区:
__NSStackBlock__
( _NSConcreteStackBlock ) - 堆区:
__NSMallocBlock__
( _NSConcreteMallocBlock )
1.1 它们最终都是继承自NSBlock
,NSBlock
则是继承自NSObject
1.2 它们位于程序的不同内存区域
-
NSGlobalBlock: 如果 Block 不访问任何 auto 变量,即它只访问全局变量或静态变量,那么该 Block 将被分配在全局数据区,并且称为全局 Block。全局 Block 的生命周期与应用程序的生命周期相同,因此可以在任何时候使用。
-
NSStackBloc: 如果 Block 访问的变量是在其定义范围内且为 auto 变量(即局部变量),那么该 Block 将被分配在栈上,并且称为栈上 Block。因为栈上 Block 的生命周期受到限制,一旦超出其定义范围,Block 就会失效。这种类型的 Block 通常是在方法内部创建,并且不需要被传递到外部使用。
-
NSMallocBlock: 当一个栈上 Block 被复制(通过调用 copy 方法)并且需要在其定义范围之外继续使用时,该 Block 将被复制到堆上,并且称为堆上 Block。堆上 Block 的生命周期由它的引用计数管理,可以在定义范围之外安全地使用,并且可以在多个地方共享。
2. 不同类型的Block
2.1 特点
block类型 | 环境 |
---|---|
NSGlobalBlock | 没有访问auto变量 |
NSStackBlock | 访问了auto变量 |
NSMallocBlock | __NSStackBlock__调用了copy |
2.2 每一种类型的block调用copy之后
3 验证
3.1 打印black类型
我们逐级打印black
的class
、superclass
,可以看到最终是继承自NSBlock
,NSBlock
则是继承自NSObject
3.1 验证不同类型的Block
我们已经知道,访问了auto变量的block是NSStackBlock
类型,__NSStackBlock__调用了copy后是NSMallocBlock
当运行如下代码,却发现访问了 auto 变量的 block 却是__NSMallocBlock__
3.2 ARC和MRC
这是因为,现在项目默认是启用ARC
的,ARC
下会自动帮我们添加copy
操作(具体规则后面章节讲),我们先把项目设置为MRC
重新运行
和我们预期的一样
4. block的copy
4.1 在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况
- block作为函数返回值时
- 将block赋值给__strong指针时
- block作为Cocoa API中方法名含有usingBlock的方法参数时
- block作为GCD API的方法参数时
4.1.1 block作为函数返回值时
通过下面示例,验证了block作为函数返回值时是__NSMallocBlock__
,直接创建打印则是__NSStackBlock__
4.1.2 将block赋值给__strong指针时
- 创建
ZSXPerson
类,并声明一个strong
修饰的block属性
@interface ZSXPerson : NSObject
@property (strong, nonatomic) void (^block)(void);
@end
- 初始化
ZSXPerson
实例对象person
,并把block赋值给person.block
此时的 block 是__NSMallocBlock__
4.1.3 block作为Cocoa API中方法名含有usingBlock的方法参数时
示例:
4.1.4 block作为GCD API的方法参数时
示例:
4.2 MRC下block属性的建议写法
@property (copy, nonatomic) void (^block)(void);
4.3 ARC下block属性的建议写法
@property (strong, nonatomic) void (^block)(void);
@property (copy, nonatomic) void (^block)(void);
@oubijiexi