美文网首页
Block学习总结(二)

Block学习总结(二)

作者: cr7aa | 来源:发表于2017-02-06 21:13 被阅读0次

    block

    1.block的实质

    之前说过其实block的本质就是"带有自动变量的匿名函数"。block类型的变量与函数指针类似,仅是将 * 号换成了 ^ 符号.那么block的底层实现又是如何。 这里可以通过 clang命令将block语法转换成底层的c语言。具体的实现: 我们可以打开终端,进入想要转换的block文件目录下,执行"clang -rewrite-objc 文件名"指令。至此就能将原文件转换成.cpp的底层文件了。

    void test() {
        void(^blk)() = ^(){
            printf("is block");
        };
        blk();
    }
    
    

    以上的代码通过clang 指令后,转换成以下的源码。(这里指摘取出与block语法相关的代码)

     struct __block_impl {
     void *isa;
     int Flags;
     int Reserved;
     void *FuncPtr;
     };
     
     static struct __test_block_desc_0 {
     size_t reserved;
     size_t Block_size;
     }
     __test_block_desc_0_DATA = { 0, sizeof(struct __test_block_impl_0)
     
     };
     
     struct __test_block_impl_0 {
     
     struct __block_impl impl;
     struct __test_block_desc_0* Desc;
     
     __test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, int flags=0) {
     impl.isa = &_NSConcreteStackBlock;
     impl.Flags = flags;
     impl.FuncPtr = fp;
     Desc = desc;
     }
     
     };
     
     static void __test_block_func_0(struct __test_block_impl_0 *__cself) {
     
     printf("is block");
     }
    
     void test() {
     
     void(*blk)() = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA));
     
     ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
     }
    
    

    首先,看到原来的test 函数转换成了

    //源代码
    void test() {
     //将block函数赋值给变量blk
        void(^blk)() = ^(){
            printf("is block");
        };
     //执行block
        blk();
    }
    
    <===>
    
    //转换后的代码
    void test() {
     
     //将block函数赋值给变量blk
     void(*blk)() = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA));
     
     //执行block
     ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
     }
     
     <===>
     //为了更好的滤清代码,去掉类型的强转
     //去除强转后的代码
    void test() {
    
     //将block函数赋值给变量blk
     *blk = & __test_block_impl_0(*__test_block_func_0, &__test_block_desc_0_DATA);
     
     //执行block
     blk->FuncPtr(blk);
    
     }
    
    

    先看 将block函数赋值给变量blk 这段代码,它的实质是将 " _ test_block_impl_0类型的实例的指针”赋值给了 “ test_block_impl_0指针类型的变量blk”。 而 _ _test_block_impl_0 是一个结构体。

    __test_block_impl_0结构体

    为什么这个结构体的名称叫 __test_block_impl_0,它的命名规则是 “ _ _ _block所在的函数的名字block_impl第几个block ”。

    void test1() {
        void(^blk)() = ^(){
            printf("is block");
        };
        blk();
        
        void(^blk2)() = ^(){
            printf("is block2");
        };
        blk2();
    }
    
    则结构体为
    
    __test1_block_impl_0
    
    __test1_block_impl_1
    
    

    接下来看看 __test_block_impl_0结构体具体的构造

     struct __test_block_impl_0 {
     
     struct __block_impl impl; //成员变量 impl
     struct __test_block_desc_0* Desc; // 成员变量 Desc
     
    //结构体的构造方法
     __test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, int flags=0) {
     impl.isa = &_NSConcreteStackBlock;
     impl.Flags = flags;
     impl.FuncPtr = fp;
     Desc = desc;
     }
     
     };
    
    

    它由2个成员变量和一个构造函数组成. 第一个成员变量impl是一个 __block_impl类型的结构体

     struct __block_impl {
     void *isa; // isa 指针
     int Flags; //标记 Flags
     int Reserved;
     void *FuncPtr; //函数指针 FuncPtr
     };
     
    

    第二个成员变量desc 是一个指向 " _ _test_block_desc_0" 结构体的指针。并且定义了一个名为__test_block_desc_0_DATA 的变量(初始化block时用到)。

     static struct __test_block_desc_0 {
     size_t reserved;
     size_t Block_size; //block大小
     }
     
     __test_block_desc_0_DATA = { 
     0, 
     sizeof(struct __test_block_impl_0) // __test_block_impl_0大小
     };
     
    

    再来看下源代码中初始化结构体的函数,将参数带入到结构体构造函数后

    & __test_block_impl_0(*__test_block_func_0, &__test_block_desc_0_DATA);
    
    <===>
    
     __test_block_impl_0(*__test_block_func_0, &__test_block_desc_0_DATA, int flags=0) {
     impl.isa = &_NSConcreteStackBlock;
     impl.Flags = flags;
     impl.FuncPtr = __test_block_func_0;
     Desc = __test_block_desc_0_DATA;
     }
    
    

    这里还需要解释一个 _test_block_func_0 的变量,它的本质是一个普通的函数,命名规则 " _ _block所在的函数名block_func第几个block"。其实它就是原block的执行部分。

     static void __test_block_func_0(struct __test_block_impl_0 *__cself) {
     printf("is block");
     }
     
     <===>
     相当于原block函数的执行部分
     ^(){
            printf("is block");
        };
    
    

    参数 _ _cself 是block本事,相当于 oc 函数中的self。(在改block中还没有用到__cself)

    block的调用

    最后就是执行block,通过上面的分析我们其实就知道了block的调用就是通过函数指针执行block函数。并将block本事作为参数传入。

     blk->FuncPtr(blk);
     <===>
     blk.impl.FuncPtr = tempFuncptr;
     *tempFuncptr = &__test_block_func_0;
     __test_block_func_0(blk);
    
    

    2.引用外部变量的实质

    当block引用了外部变量的时候,它与没有引用外部变量的时候,通过clang转换的源码来看是有不同的。例如,下面的这段引用了外部变量的block

    void testBlock2() {    
        int a = 10;
        int b = 20;
        void(^blk)() = ^(){
            printf("a = %d",a);
        };
        blk();
    }
    
    

    通过 clang指令,它将转换成了如下的代码

     <=== 以下代码与未使用外部变量有区别 ===>
     void testBlock2()
     int a = 10;
     int b = 20;
     void(*blk)() = ((void (*)())&__testBlock2_block_impl_0((void *)__testBlock2_block_func_0, &__testBlock2_block_desc_0_DATA, a));
     ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
     }
     
     struct __testBlock2_block_impl_0 {
     struct __block_impl impl;
     struct __testBlock2_block_desc_0* Desc;
     int a;
     __testBlock2_block_impl_0(void *fp, struct __testBlock2_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
     impl.isa = &_NSConcreteStackBlock;
     impl.Flags = flags;
     impl.FuncPtr = fp;
     Desc = desc;
     }
     };
    
     static void __testBlock2_block_func_0(struct __testBlock2_block_impl_0 *__cself) {
     int a = __cself->a; // bound by copy
     printf("a = %d",a);
     }
     
    <=== 以下代码与未使用外部变量相同 ===>
     struct __block_impl {
     void *isa;
     int Flags;
     int Reserved;
     void *FuncPtr;
     };
     
     static struct __testBlock2_block_desc_0 {
     size_t reserved;
     size_t Block_size;
     } __testBlock2_block_desc_0_DATA = { 0, sizeof(struct __testBlock2_block_impl_0)};
    

    来对比一下与未使用外部变量时区别的部分,首先是__testBlock2_block_impl_0这个结构体,它多出了一个与引用的外部变量相同类型的成员变量。(这里只添加了block内部使用了的外部变量a这一个成员变量,外部变量b没有使用,所以没有添加到结构体中):

     struct __testBlock2_block_impl_0 {
     struct __block_impl impl;
     struct __testBlock2_block_desc_0* Desc;
     int a;  //引用的外部变量 追加成了 __testBlock2_block_impl_0 结构体的成员变量
     };
    

    再来看一下__testBlock2_block_impl_0实例初始化的方法:

     __testBlock2_block_impl_0(void *fp, struct __testBlock2_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
     impl.isa = &_NSConcreteStackBlock;
     impl.Flags = flags;
     impl.FuncPtr = fp;
     Desc = desc;
     }
    

    这个初始化方法与未使用外部变量时的区别是多了一个参数a,而: a(_a)这个语法是c++中结构体中给const类型的变量初始化的意思。即将参数_a赋值给结构体的变量a。

    相关文章

      网友评论

          本文标题:Block学习总结(二)

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