OC基础-Block1

68 阅读2分钟

「这是我参与2022首次更文挑战的第23天,活动详情查看:2022首次更文挑战」。

OC基础- block
Block:本质:一个OC对象,一个封装了函数调用以及函数调用环境的OC对象

先定义一个简单的block,上代码

    Int age = 23;
        void (^newBlock)(int a, int b) = ^(int a,int b){
            NSLog(@"测试Block1 a = %d",age);
            NSLog(@"测试Block2 b = %d",b);
        };

将其转化成.cpp文件以便查看源码
xcrun -sdk iphones clang -arch arm64 -rewrite-objc main.m

我们得到刚才那一段代码的本质

    int age = 10;
    //定义block对象
        void (*newBlock)(int a, int b) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
    //执行block内部代码
        ((void (*)(__block_impl *, int, int))((__block_impl *)newBlock)->FuncPtr)((__block_impl *)newBlock, 10, 30);

经过筛选得出来
block组成部分

         struct __main_block_impl_0 {
           struct __block_impl impl;
           void *isa;
            int Flags;
            int Reserved;
            void *FuncPtr;
           struct __main_block_desc_0* Desc;
           int age; //=>访问外部的age  
           __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
             impl.isa = &_NSConcreteStackBlock;
             impl.Flags = flags;
             impl.FuncPtr = fp;
             Desc = desc;
           }
         };
其中__block_impl结构如下所示
    struct __block_impl {
            void *isa;
            int Flags;
            int Reserved;
            void *FuncPtr;
            };

其中struct __main_block_desc_0 里面结构如下(描述信息)
    struct __main_block_desc0{
        size_t reserved;
            size_t Block_size;
    };

从main函数可以看出来定义block的时候是直接把age值传进去的。

综合,
1.为什么说block是一个OC对象,因为内部有isa指针
2.为什么说block一个封装了函数调用以及函数调用环境的OC对象
看下图

879bf09724de9777086039a601287cc5_webp.webp

block内部只有两个参数,一个impl,一个Desc,而函数的调用地址 - FuncPtr是再impl中的,为什么这里能直接这样写呢?

因为,__main_block_impl_0 结构的地址和他的第一个成员一样,第一个成员的地址是__block_impl,所以__main_block_impl_0 和 __block_impl 的地址其实是同一个,通过格式强制转换,将 main_block_impl_0 转成 block_impl 就可以直接拿到他内部的 FuncPtr 函数地址,然后进行调用!

OC基础-Block(2)Block的变量捕获
为了保证block内部能够正常访问外部的变量,block有个变量捕获机制

Block捕获机制

接下来一一验证

auto类型是一种默认类型
auto类型局部变量(值传递)(原因,auto类型会随时销毁,所以趁它有值的时候就获取进来)

       int age = 10;  相当于 auto int age = 10;
        
        void (^newBlock)(void) = ^(void){
            NSLog(@"测试Block1 age = %d",age);
        };
        
        age = 30;
        
        newBlock();

打印结果

2022-02-23 10:32:54.982175+0800 DoBlock[69542:1863431] 测试Block1 age = 10

查看源码
···
int age = 10;

    void (*newBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));

    age = 30;

    ((void (*)(__block_impl *))((__block_impl *)newBlock)->FuncPtr)((__block_impl *)newBlock);

···