美文网首页
block源码分析一

block源码分析一

作者: 帽子和五朵玫瑰 | 来源:发表于2018-06-05 10:03 被阅读0次
    #include "stdio.h"  
    int main()  
    {  
        void (^block)(void) = ^(void)  
        {printf("aaa\n");};  
        block();  
        return 0;  
    }  
    
    //那两行代码竟然增加到了那么多,可以从里面看出来,最终block匿名函数会用C语言的函数指针来处理
    
    struct __block_impl {  
        void *isa;  
        int Flags;  
        int Reserved;  
        voidvoid *FuncPtr;  
    };  
      
    struct __main_block_impl_0 {  
      struct __block_impl impl;  
      struct __main_block_desc_0* Desc;  
      __main_block_impl_0(voidvoid *fp, struct __main_block_desc_0 *desc, int flags=0) {  
        impl.isa = &_NSConcreteStackBlock;  
        impl.Flags = flags;  
        impl.FuncPtr = fp;  
        Desc = desc;  
      }  
    };  
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {  
    printf("aaa\n");}  
      
    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()  
    {  
     void (*block)(void) = ((void (*)())&__main_block_impl_0((voidvoid *)__main_block_func_0, &__main_block_desc_0_DATA));  
     ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);  
     return 0;  
    }  
    

    源码结构分析部分

    1.block实际的结构体部分(本体)

    struct __main_block_impl_0 {  
      struct __block_impl impl;  
      struct __main_block_desc_0* Desc;  
      __main_block_impl_0(voidvoid *fp, struct __main_block_desc_0 *desc, int flags=0) {  
        impl.isa = &_NSConcreteStackBlock;  
        impl.Flags = flags;  
        impl.FuncPtr = fp;  
        Desc = desc;  
      }  
    };  
    

    首先impl和Desc也是两个结构体,而__main_block_imp_0就是该结构体的构造函数,用来初始化,后面细细介绍

    2.我们先来看看第一个成员变量impl

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

    在objc中,id代表了一个对象。根据上面的声明,凡是首地址是isa的struct指针,都可以被认为是objc中的对象
    isa指针,这个很熟悉吧,这就能看出其实block其实就是对象

    他的第一个属性也是一个结构__block_impl,而第一个参数也是一个isa的指针。在运行时,NSObject和block的isa指针都是指向对象的一个8字节。NSObject及派生类对象的isa指向Class的prototype,而block的isa指向了_NSConcreteStackBlock这个指针。就是说当一个block被声明的时候他都是一个_NSConcreteStackBlock类的对象。

    flags和reserved这两个基本就是某些标记(暂时没那么重要)FuncPtr这个就是函数指针,也就是block所需要执行的代码段,真正存的地址

    3.第二个成员变量Desc

    static struct __main_block_desc_0 {  
      size_t reserved;  
      size_t Block_size;  
    }  
    

    顾名思义reserve和Block_size分别代表了版本升级所需的区域和Block的大小

    4.第三个就是这个结构体的构造函数(可以理解为对象的初始化方法)

    __main_block_impl_0(voidvoid *fp, struct __main_block_desc_0 *desc, int flags=0) {  
        impl.isa = &_NSConcreteStackBlock;  
        impl.Flags = flags;  
        impl.FuncPtr = fp;  
        Desc = desc;  
      }  
    

    可以通过这里看出,初始化的时候会右fp指针(这里就是函数指针),以及desc结构体的指针传进来初始化,void fp的指针赋值给了FuncPtr指针,以上的结构和初始化函数就是基本的Block生成的源代码

    源码调用部分分析

    5.现在来看看Main函数中调用的基本转换(初始化转换)

    void (*block)(void) = ((void (*)())&__main_block_impl_0((voidvoid *)__main_block_func_0, &__main_block_desc_0_DATA));  
    
    // 调用结构体函数初始化  
    struct __main_block_impl_0 impBlock = __main_block_impl_0(__main_block_func_0,&__main_block_desc_0_DATA);  
      
    // 赋值给该结构体类型指针变量  
    struct __main_block_impl_0 *block = &impBlock;  
    

    就是把栈上生成的__main_block_impl_0的结构体实例的指针,赋值给__main_block_impl_0结构体指针类型的变量block

    __main_block_func_0就是转换成的函数指针,这样void (*block)(void) = ^{}这句简单的代码最终就是上面的结构体初始化函数的内部实现逻辑

    6.再来细看下初始化函数内部的实现__block_main_impl_0

    __main_block_impl_0(__main_block_func_0,&__main_block_desc_0_DATA);  
    

    第一个参数是由Block块语法转换的正真内部函数指针,第二个参数就是作为静态全局变量初始化__main_block_desc_0的结构体实例指针,初始化如下

    __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};  
    

    最终,初始化内部进行赋值

    isa = &_NSConcreteStackBlock
    
    Flags = 0
    
    Reversed = 0
    
    FuncPtr = __main_blcok_func_0(就是Block块代码转换成的C语言函数指针)
    
    Desc = &__ main_block_desc_0_DATA
    

    7.最终block()调用的内部实现

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

    去掉类型就是:

    (*block->imp.FuncPtr)(block);
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {  
    printf("aaa\n");} 
    

    最后就是直接调用函数指针进行最终的调用,由上面所描述的,FuncPtr就是由__main_block_func_0的函数指针所赋值的指针,而且可以看出,这个函数的参数正是指向block本身结构体实例,block作为参数进行了传递

    objc_object结构体有个Class isa,这个isa指向下面这个结构体

    struct objc_class {
    
        Class isa  OBJC_ISA_AVAILABILITY;
    
    
    
    #if !__OBJC2__
    
        Class super_class                                        OBJC2_UNAVAILABLE;
    
        constchar *name                                         OBJC2_UNAVAILABLE;
    
        long version                                             OBJC2_UNAVAILABLE;
    
        long info                                                OBJC2_UNAVAILABLE;
    
        long instance_size                                       OBJC2_UNAVAILABLE;
    
        struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    
        struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    
        struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    
        struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
    
    #endif
    
    
    
    }
    

    isa指针__NSConcreteStackBlock相当于class的结构体实例,关于该类的所有信息都包含在这个地址里面,跑起来的时候就可以通过该指针去父类找需要的东西了,那么Block最简单的实质应该有个初步了解了吧,明白了Block其实就是OC里面的对象了

    概括下:

    1. __main_block_impl_0这个就是Block内部的结构体(该结构体成员有impl,Desc 和一个该结构体的初始化函数)

    2.Block块内的代码转换成了__main_block_func_0的c语言函数(参数就是__main_block_impl_0结构体)

    3.Block语法的初始化实际就是将__main_blcok_impl_0的结构体实例化,重点是Block的代码块{}通过转换成C的函数指针进行结构体的成员变量FuncPtr指针赋值

    4.Block调用就是通过实例化的结构体里面的FuncPtr指针就行函数调用,而且参数就是该结构体本身(暂时没用到这个传出去的结构体,另外写一个介绍带参数是如何完成回调的,如何截获变量等等)

    相关文章

      网友评论

          本文标题:block源码分析一

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