又见block(三):block实质

首先来看看block截获自动变量(外部局部变量)的几种情况

block“带有自动变量(截获局部变量)”的含义在于具有“截获自动变量值”的能力

ARC中常见的block用法场景如下:
这里写图片描述
由以上结果总结如下:

  • 默认block的类型是NSGlobalBlock,一旦在block中引用自动变量(包括OC对象),无论是否修改,block类型变为NSMallocBlock
  • 默认情况下,在block内不能对截获的自动变量(或OC对象)进行修改,编译器会报错
  • 默认情况下,在block中引用的自动变量或OC对象(包括调用OC对象方法),block中的自动变量地址不同,而引用前后自动变量的地址一致
  • 在自动变量(或OC对象)前添加__block,无论是否修改自动变动,引用后与block自动变量的地址一致,而且与引用前不一致

另外,需要注意的一点是,block目前没有实现对C语言数组的截获,例如以下代码会产生编译错误
这里写图片描述
使用指针可以解决这个问题

- (void)blockCaptureCArray {
    //  C 语言的数组
    const char *text = "hello";
    void (^blockTest) (void) = ^{
        NSLog(@"%c",text[0]);
    };
    blockTest();
    }

block的实质

可以通过clang -rewrite-objc 源代码文件名 将含有block的代码变换为C++的源代码进行分析,将如下main.m文件

#import <stdio.h>
    int main() {
        void (^blk) (void) = ^{
            printf("Block\n");
        };
        blk();
        return 0;
    }

打开终端,在main.m所在目录下键入clang -rewrite-objc main.m即可在当前目录下生成一个main.cpp文件,主要分为:

1、__block_impl结构体定义

    //  结构体 __block_impl 的声明
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

2、__main_block_impl_0结构体定义,其中包含其构造函数

参数fp传入__main_block_func_0函数的地址,赋值给__block_impl类型的结构体实例impl的函数指针FuncPtr
参数desc传入的&__main_block_desc_0_DATA结构体取地址赋值给了__main_block_desc_0类型的结构体指针Desc
__block_impl类型的结构体实例implisa指针存放了_NSConcreteStackBlock类的地址,旨在将block作为OC的对象处理,将block类的信息放置于_NSConcreteStackBlock,由此可见,block的实质是OC对象

    //  结构体 __main_block_impl_0 的声明
struct __main_block_impl_0 {
    //  成员变量 impl
  struct __block_impl impl;
    //  指针
  struct __main_block_desc_0* Desc;

    //  结构体 __main_block_impl_0 的构造函数
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

3、block函数转化后的源代码

    //  __cself 是 __main_block_impl_0 结构体的指针
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

        printf("Block\n");
    }
    //  block的描述
static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
}
__main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)
};

4、程序执行的main函数

/* main.m 中 main 函数转换后的源代码*/
int main() {
    //  左边是一个无参无返回值的函数指针
    //  右边__main_block_impl_0结构体包含自己的属性、构造方法、成员方法,然后取其地址
    void (*blk) (void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    //  代码转换1
    struct __main_block_impl_0 tmp = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
    struct __main_block_impl_0 *blk = &tmp;

    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    // 对应代码 block();
    // 前半部分访问结构体 __block_impl 自己的函数指针 FuncPtr,并且将 block 自身作为参数传递
    //  代码转换2
    (*blk->impl.FuncPtr)(blk);
    return 0;
}

又见block(二):block语法定义
又见block(四):block捕获自动变量
又见block(五): __block变量和对象
又见block(六):block存储域与__block变量存储域
又见block(七):截获对象

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值