美文网首页
iOS--block的本质和变量捕获机制

iOS--block的本质和变量捕获机制

作者: 人间四月天_Andy | 来源:发表于2021-02-24 16:31 被阅读0次

学习笔记,如有错误,欢迎批评指正!!! 仅供学习交流...

block的本质

  1. block是带有自动变量(局部变量)的匿名函数(不带名称的函数)
    1. 带有自动变量的值:block保持自动变量的值。
  2. block的本质是一个OC对象,它内部有一个isa指针。

直接上代码,查看block的底层结构

void (^block1)(void) = ^{
            NSLog(@"This is block!!!");
        };

接下来,使用终端命令:

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o xxx.cpp

然后,查看xxx.cpp文件,如下图所示(请忽略文件命名 🤣...)

1.jpg
接下来,我们把重点代码复制下来
//block转化成c++的结构体
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  //构造函数(类似于OC的init方法)
  __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;
  }
};

////封装了 block执行逻辑的函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_zt_xy2f_gsx0mlbrzmvts93gjqm0000gn_T_main_b2615a_mi_0);
        }

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)};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        //定义block变量
        void (*block1)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));

        //执行block内部的代码
        ((void (*)(__block_impl *))((__block_impl *)block1)->FuncPtr)((__block_impl *)block1);
    }
    return 0;
}

对 main 函数里边的代码 进行简化:

//定义block变量
void (*block1)(void) = &__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA);

//执行block内部的代码
((void (*)(__block_impl *))((__block_impl *)block1)->FuncPtr)((__block_impl *)block1);

继续简化:

//定义block变量
//调用 __main_block_impl_0 构造函数,返回结构体对象,最后取地址,复制给 block1
//返回的结构体对象就是 struct __main_block_impl_0
void (*block1)(void) = &__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA);

//执行block内部的代码
block1->FuncPtr(block1);

block的底层机构大致就是这样的:

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  //构造函数(类似于OC的init方法)
  __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;
  }
};

接下来,我们看一下这段代码:

void (*block1)(void) = &__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA);

可以看到,__main_block_impl_0 函数传递了两个参数 (void *)__main_block_func_0&__main_block_desc_0_DATA

第一个参数 (void *)__main_block_func_0,这个函数里边封装的就是 block的执行逻辑代码

最终 这个函数指针赋值给了 struct __block_impl里边的void *FuncPtr。也就是说 FuncPtr里边存储的就是将来要执行的 block函数的地址。

2.jpg
接着,我们看 block的执行代码
((void (*)(__block_impl *))((__block_impl *)block1)->FuncPtr)((__block_impl *)block1);

//简化之后
block1->FuncPtr(block1);

可以看到,执行block内部的代码,其实是找到 FuncPtr指针,然后去执行。

block1之所以可以直接找到 FuncPtr,是因为 将 block1强制转换成了 __block_impl 这种类型,然后再找到 __block_impl里边的 FuncPtr

__main_block_impl_0 这种类型的block 之所以 能够转换成为 __block_impl类型,是因为 __main_block_impl_0里边的第一个成员就是 __block_impl类型的,这两个的地址是一样的。

从另一种方面来说,由于__main_block_impl_0类型的第一个成员的类型是 struct __block_impl,所以 __main_block_impl_0也可以直接看成是如下结构:

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __main_block_impl_0 {
  //struct __block_impl impl;
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
  struct __main_block_desc_0* Desc;
  //构造函数(类似于OC的init方法)
  __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;
  }
};

这样,就可以简单明了的理解 block为什么可以直接找到 FuncPtr了。

其实,我们还可以通过以下代码查看 block

void (^block1)(void) = ^{
            NSLog(@"This is block!!!");
        };
        
block1();
              
NSLog(@"%@ %@ %@ %@",[block1 class]
                      ,[[block1 class] superclass]
                      ,[[[block1 class] superclass] superclass]
                      ,[[[[block1 class] superclass] superclass] superclass]);
输出结果: 3.jpg

可以看到,block最终继承自 NSObject,再次证明 block其实就是一个OC对象

变量的捕获

为了保证 block 内部能够正常访问 外部变量的值, block有个变量捕获机制

C语言的函数中可能使用的变量

  • 自动变量(局部变量)-- 离开作用域就会自动销毁
  • 函数的参数
  • 静态局部变量
  • 静态全局变量
  • 全局变量

其中,静态局部变量静态全局变量全局变量,虽然这些变量的作用域不同,但是在整个程序当中,一个变量总保持在一个内存区域。因此,即使在多个函数中使用这些变量,这些变量的值总是保持不变。

那么,哪些变量可以被捕获到block内部呢?先看下边的结论

局部变量会被捕获到block内部,全局变量不会被捕获到block内部。

局部变量的捕获

直接上代码: 4.jpg

定义了 两个变量 a 和 b, 运行,查看输出结果

02-block变量捕获[2020:161392] a = 10, b = 20

接着看转换的c++代码。

5.jpg
可以看出,变量a 和 变量b 都被捕获到了 block 内部。并且,变量a 和 变量b 的捕获方式是一模一样的,都是直接把值传递进去(值传递)。
//这两句代码是等效的  默认前边都是有一个 auto 关键字
int a = 10; 
auto int a = 10; 

接下来,修改代码,如下图:

6.jpg
从运行输出结果可以看出 static修饰的局部变量 和 auto修饰的局部变量的结果是不一样的。

重新生成一下xxx.cpp代码,查看一下:

7.jpg
可以看出static修饰的局部变量 传递的是 地址值,被block捕获到内部的是一个指针。(指针传递)

关于局部变量的捕获:

  1. auto类型:会被捕获, 捕获方式:值传递
  2. static类型:会被捕获, 捕获方式:指针传递

全局变量的捕获

继续修改代码,如下如所示:

8.jpg

全局变量a 和 静态全局变量b 获取到的都是最新的值。

重新生成cpp文件

9.jpg
可以看出,全局变量a 和 静态全局变量b 并没有被捕获到 block内部。

总结:

10.jpg

结论 11.jpg

相关文章

  • iOS--block的本质和变量捕获机制

    学习笔记,如有错误,欢迎批评指正!!! 仅供学习交流... block的本质 block是带有自动变量(局部变量)...

  • Block本质解密---变量捕获机制

    在做项目的时候常用到block, 最近看了一些资料, 对block的有了更深入的理解, 下面记录下。 一、Bloc...

  • block变量的捕获(capture)

    ?为了保证block内部能够正常访问外部变量,block有个变量捕获机制 auto变量的捕获

  • OC中的Block(二)

    block的变量捕获(capture) 为了保证block内部能够正常访问外部的变量,block有个变量捕获机制 ...

  • Block 之 变量捕获

    为了保证block内部能够正常访问外部的变量,block有个变量捕获机制,即捕获外部变量。 前言: 搞清成员变量、...

  • Objective - C block(二)block的类型及捕

    (一)block 捕获变量类型 为了保证block内部能够正确访问外部的变量,block有一个变量捕获机制 (1)...

  • iOS开发-7.Block

    1.block的本质 2.block的变量捕获(capture) 3.auto变量的捕获image 4.block...

  • Block如何捕获外部变量一:基本数据类型

    上一篇我们介绍了Block的本质(想要了解,点击这里传送门),这一篇,我们详细讲解Block捕获外部变量的机制.我...

  • iOS:Block(一)

    目录一,本质二,变量捕获三,类型四,对象类型的auto变量 一,本质 1,实例代码 2,底层代码(用clang进行...

  • 08-Block

    一、block本质: 二、变量捕获 auto :自动变量(局部变量),离开作用域就会销毁。 (我们声明的变量 au...

网友评论

      本文标题:iOS--block的本质和变量捕获机制

      本文链接:https://www.haomeiwen.com/subject/zcmsfltx.html