[OC学习笔记]Block三种类型

Block的三种类型

block的类型,取决于isa指针,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型。
使用如下代码:

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

打印结果:
请添加图片描述
三个block的类型分别为:__NSGlobalBlock____NSMallocBlock____NSStackBlock__上述三种类型最终都是继承自NSBlock,而NSBlock又是继承自NSObject:此处又进一步说明block其实就是一个OC对象。

  • NSMallocBlock(_NSConcreteMallocBlock) 对象存储在堆区
  • NSStackBlock(_NSConcreteStackBlock) 对象存储在栈区
  • NSGlobalBlock(_NSConcreteGlobalBlock)对象存储在数据区

换到MRC环境下试一下:
请添加图片描述
MRC模式下,三种block类型:__NSGlobalBlock____NSStackBlock____NSStackBlock__,为什么中间的类型由malloc变成了stack?这是因为ARC自动帮助我们对block进行了copy操作。

分析

我们先尝试转化C++代码查看类型:

struct __testBlockType_block_impl_0 {
  struct __block_impl impl;
  struct __testBlockType_block_desc_0* Desc;
  __testBlockType_block_impl_0(void *fp, struct __testBlockType_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
struct __testBlockType_block_impl_1 {
  struct __block_impl impl;
  struct __testBlockType_block_desc_1* Desc;
  int age;
  __testBlockType_block_impl_1(void *fp, struct __testBlockType_block_desc_1 *desc, int _age, int flags=0) : age(_age) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
struct __testBlockType_block_impl_2 {
  struct __block_impl impl;
  struct __testBlockType_block_desc_2* Desc;
  int age;
  __testBlockType_block_impl_2(void *fp, struct __testBlockType_block_desc_2 *desc, int _age, int flags=0) : age(_age) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

发现都是_NSConcreteStackBlock类型。不能正确反应block的实质类型(可能是LLVM编译器版本的问题,而clang又是LLVM的一部分)。
程序分区除了堆区需要程序员手动管理内存外,其他区都由系统自动管理。

{
    //等号左边:auto形局部变量,存放在栈区;
    //等号右边:常量,存放在数据区;
    int age = 10;
    //等号左边:auto形局部变量(指针),存放在栈区;
    //等号右边:auto形局部变量地址,存放在栈区;
    int *agePtr = &age;
    //等号左边:auto形局部变量(指针),存放在栈区;
    //等号右边:alloc开辟的对象,存放在堆区;
    NSObject *objc = [[NSObject alloc] init];
}

三种block类型(global、malloc、stack),从字面理解,可以推断依次存放在数据区、堆区、栈区;blcok1没有访问任何变量,后两个block都访问量变量age,而age是一个auto类型的局部变量。

int height = 150;

void testBlockType(void) {
    int age = 10;
    static int weight = 80;
    
    void(^block1)(void) = ^{
        NSLog(@"block1----");
    };
    
    void(^block2)(void) = ^{
        NSLog(@"block2----%d", age);
    };
    
    void(^block3)(void) = ^{
        NSLog(@"-----%d", weight);
    };
    
    void(^block4)(void) = ^{
        NSLog(@"-----%d", height);
    };
        
    NSLog(@"%@ %@", [block3 class], [block4 class]);
}

结果:
请添加图片描述
如果是static修饰的局部变量,或者访问全局变量,则block的类型都是__NSGlobalBlock__,那么基本上可以肯定,block的类型可以取决于其访问的变量的属性。
因为auto类型的局部变量是存放在栈区的,而block要访问该变量,经前面分析,block会将该变量捕获到block结构体内部,即重新开辟内存来存放该局部变量(相当于copy操作,但不是copy),那么此时的block自己是存放在哪个区呢?前面说了,auto类型的局部变量一定是存放在栈区的,这点毋庸置疑,而block虽然新开辟内存来存放该变量,但改变不了该变量是一个auto类型的局部变量的属性,因此此时的block也只能存放在栈区;既然存放在栈区,则访问的变量作用域仅限于离其最近的大括号范围内,超出则被自动释放。
MRC下测试下面代码:

void(^block4)(void);

void test3()
{
    int age = 10;
    
    block4 = ^{
        NSLog(@"----%d", age);
    };
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        test3();
        block4();
//        NSLog(@"%@", [block4 class]);

    }
    return 0;
}

输出:
请添加图片描述
说明age已经被自动释放,block再次调用时,访问的是被废弃的内存。手动copy

void(^block4)(void);

void test3()
{
    int age = 10;
    
    block4 = [^{
        NSLog(@"----%d", age);
    } copy];
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        test3();
        block4();
        NSLog(@"%@", [block4 class]);
    }
    return 0;
}

结果:
请添加图片描述
因为copy是把age的值直接拷贝到了一块新的内存区域,而我们知道copy操作开辟的内存必定是在堆区(同时,block的类型由之前的__NSStackBlock__类型变为__NSMallocBlock__类型)。因此,防止一个auto类型的局部变量自动释放的方法,就是将其copy到堆区进行手动管理,达到对其生命周期可控的目的(所以记得要释放[block release])——这是MRC模式下的手动管理内存,而在ARC模式下系统会自动管理内存(copyrelease)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值