block的类型
block有3种类型,可以通过调用class
方法或者isa
指针查看具体类型,最终都是继承自NSBlock类型
NSGlobalBlock ( _NSConcreteGlobalBlock )
NSStackBlock ( _NSConcreteStackBlock )
NSMallocBlock ( _NSConcreteMallocBlock )
// 一切以运行时的结果为准
// clang 编译的C++代码,不一定是最终结果,只作为参考
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
// block类型
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
验证block最终是一个Objective-C对象
void(^myBlock)(void) = ^{
NSLog(@"===");
};
NSLog(@"%@",[myBlock class]); // __NSGlobalBlock__
NSLog(@"%@",[[myBlock class] superclass]); // __NSGlobalBlock__
NSLog(@"%@",[[[myBlock class] superclass] superclass]); // NSBlock
NSLog(@"%@",[[[[myBlock class] superclass] superclass] superclass]);// NSObject
这也就说明了,为什么block会有isa
指针,是从NSObject
继承过来的
面试题:为什么block内部会有isa
指针变量???
- 程序区域.text区: 一般存放代码段(由编译器管理,不需要手动管理)
- 数据区域.data区: 一般存放全局变量(由编译器管理,不需要手动管理)
- 堆: 动态分配的内存,需要程序员手动申请内存,程序员自己管理内存
- 栈: 放一些局部变量,系统会自动分配内存
- ARC环境下,只要访问了auto变量,那么类型就是__NSMallocBlock__
- MRC环境下,只要访问了auto变量,那么类型就是__NSStackBlock__
- 反之,如果只访问了static修饰的局部变量,或者全局变量都是__NSGlobalBlock__
// 没有访问auto变量,block放在数据段
void(^myBlock)(void) = ^{
// NSLog(@"a = %d, b = %d, age = %d", a, b,age);
NSLog(@"a = %d");
};
// 访问auto变量,block放在栈上,会捕获这个变量,变量超出作用域就会销毁
auto int a = 1;
void(^myBlock1)(void) = ^{
NSLog(@"a = %d", a);
};
NSLog(@"%@ %@",[myBlock class],[myBlock1 class]);
面试题
如下代码,在MRC环境下,打印什么?
// MRC环境下
void(^myBlock)(void);
void test() {
auto int a = 1;
myBlock = ^{
NSLog(@"a = %d", a);
};
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
test();
myBlock();
}
}
在MRC环境下,myBlock存储在栈上,作用域一过,变量a就会销毁,那么a就成乱七不糟的值.比如打印:a = -272632456
那么如何才能打印1?
在ARC环境下,打印为1
block定义时候进行copy操作,那么myBlock从栈复制到堆,内存就会保存到堆上,而不是栈上
// MRC环境下
void(^myBlock)(void);
void test() {
auto int a = 1;
myBlock = [^{
NSLog(@"a = %d", a);
} copy];
NSLog(@"%@",[myBlock class]);
}
注意:MRC环境下
打印类型发现由原来的__NSStackBlock__
类型,变成了__NSMallocBlock__
类型了,因此,定义block,这个block执行在其他地方,为了保住这个block的命,使用copy操作就会由栈复制到堆上
如果是全局类型__NSGlobalBlock__
block,那么进行copy后,类型会变吗?
void(^myBlock)(void) = [^{
NSLog(@"=====");
} copy];
NSLog(@"%@",[myBlock class]); // __NSGlobalBlock__
打印结果仍然是全局类型__NSGlobalBlock__
block类型
block类型 | block操作 | 运行环境 |
---|---|---|
NSGlobalBlock | 没有访问auto变量 | MRC/ARC |
NSStackBlock | 访问了auto变量 | MRC |
NSMallocBlock | __NSStackBlock__调用了copy | MRC |
NSMallocBlock | 访问了auto变量 | ARC |
每一种类型的block调用copy后的结果如下,MRC环境下
block的类 | 副本源的配置存储域 | 复制效果 |
---|---|---|
_NSConcreteStackBlock | 栈 | 从栈复制到堆 |
_NSConcreteGlobalBlock | 程序的数据区域 | 什么也不做 |
_NSConcreteMallocBlock | 堆 | 引用计数增加 |