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指针变量???

demo

// 没有访问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 引用计数增加