iOS底层系列之<10>--Block(三)Block的类型

本文详细探讨了Block的本质,指出Block实际上是一个Objective-C对象,可以分为全局、栈上和堆上的三种类型。着重分析了栈上(NSStackBlock)Block的特点,包括其生命周期和内存管理,并通过代码示例解释了栈Block在不同情况下如何访问局部变量及其后果。最后,讨论了使用`copy`关键字对Block的影响,以及在ARC和非ARC环境下Block类型的差异。
摘要由CSDN通过智能技术生成

1、block本质是什么?

- (void)testBlock {
   
    void (^block1)(void) = ^(){
        NSLog(@"hello");
    };
    
    NSLog(@"%@",[block1 class]);
    NSLog(@"%@",[[block1 class] superclass]);
    NSLog(@"%@",[[[block1 class] superclass] superclass]);
}

打印结果:
2022-01-01 06:27:55.056968+0800 Interview01-kvo[43991:11810676] NSGlobalBlock
2022-01-01 06:27:55.057261+0800 Interview01-kvo[43991:11810676] NSBlock
2022-01-01 06:27:55.057430+0800 Interview01-kvo[43991:11810676] NSObject

所以block本质是一个OC对象。

2、block的类型

- (void)testBlock {
    int age = 10;
    void (^block1)(void) = ^(){
        NSLog(@"hello");
    };
    
    void (^block2)(void) = ^(){
        NSLog(@"hello-%d",age);
    };
    
    
    
    NSLog(@"%@ %@ %@",[block1 class], [block2 class], [^{
        NSLog(@"%d",age);
    } class]);
}

打印结果共3种:
NSGlobalBlock : 全局的 ,条件–>没有访问auto变量的
NSStackBlock:栈里面的 ,条件 --> 访问了auto变量的
NSMallocBlock : 堆里面的,条件 —> NSStackBlock调用了copy

  • 关闭ARC
  • 搜索:automatic ref–> 设置为no
    在这里插入图片描述
  • 新建命令行项目,关闭ARC后
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int age = 10;
        void (^block1)(void) = ^(){
            NSLog(@"hello");
        };
        
        void (^block2)(void) = ^(){
            NSLog(@"hello-%d",age);
        };
        
        
        
        NSLog(@"%@ %@ %@",[block1 class], [block2 class], [^{
            NSLog(@"%d",age);
        } class]);
    }
    return 0;
}

打印结果:
2022-01-01 06:57:01.439787+0800 block[44204:11829604] NSGlobalBlock
NSStackBlock
NSStackBlock
Program ended with exit code: 0

关闭ARC后,可以看到,block2是访问了auto变量,类型为stack

3、重点研究stack类型

  • Global类型没太大意义,完全可以用一个普通的函数代替。
  • 所以这里重点研究stack类型。
  • 先看下下面这段代码会打印什么(MRC环境)
#import <Foundation/Foundation.h>

void (^block)(void);

void test() {
    int age = 10;
    block = ^{
        NSLog(@"hello ---- %d",age);
    };
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        test();
        
        block();
    }
    return 0;
}
// 第一步来到这里
void test() {
    int age = 10;
    block = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA, age));
}

// 第二步会来到这里
struct __test_block_impl_0 {
  struct __block_impl impl;
  struct __test_block_desc_0* Desc;
  int age; // 函数作用域结束后,栈里面的age的值被释放了
  __test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

// 第三步会来到这里
static void __test_block_func_0(struct __test_block_impl_0 *__cself) {
  int age = __cself->age; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_qw_rxjvg1rx6hs778r_253ckdt40000gn_T_main_23a55d_mi_0,age);
    }



int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        test();
		
		// 第四步来到这里
		// test()方法执行完,函数作用域释放了
		// 因此,test()函数的栈此刻已经被释放了
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
        // 所以,最终打印结果是:
     //   2022-01-01 07:12:01.415275+0800 block[44249:11837159] hello ---- -272632584

    }
    return 0;
}

  • 将以上代码改动下
void (^block)(void);

void test() {
    int age = 10;
    block = [^{
        NSLog(@"hello ---- %d",age);
    } copy]; // 增加copy
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        test();
        
        block();
    }
    return 0;
}
  • 此刻将block拷贝到内存中
  • 因此打印结果是

hello ---- 10
而block的类型也成为malloc类型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值