zoukankan      html  css  js  c++  java
  • 技术分享-开发利器block底层实现

    block的底层实现

    在上一篇分享中,主要介绍了block得使用注意和一些经验心得以及开发中的一些使用场景.详情请看<技术分享-开发利器block>

    那么,在block的使用注意中,你们有没有疑问说,为什么会这样呢?
    说到为什么,就涉及到block的底层实现了,下面,我来介绍一下block的底层实现 - .-

    block注意事项

    int a = 10;
     void (^myBlock)() = ^{
      NSLog(@"a = %i", a);
       } myBlock();
        输出结果: 10
    
    • 等于10,结果显而易见,但是为什么呢?看完下一段的代码再作解析...
    __block int a = 10;
    void (^myBlock)() = ^{
      NSLog(@"a = %i", a);
      };
      a = 20;
    myBlock();
    输出结果: 20
    
    • 我在block块的外部添加了a = 20;,那么为什么可以把a的值改掉呢?

    我们来看看block的底层实现:

    使用clang命令:clang -rewrite-objc main.m,把刚才的main.m编译为c++文件.
    编译好之后,你会发现文件夹多了一个main.cpp文件,这个就是编译出来的c++文件了,好,我们open一下.

    *打开之后你会发现,哇塞,有10W+行代码...- -!
    好,我们拖到最下面,编译结果如下:

    #---------------编译器内会将block内部生成对应的函数---------------------------------------------------------
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      int a;
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    #---------------编译器内会将block内部生成对应的函数---------------------------------------------------------
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      int a = __cself->a; // bound by copy
    
                NSLog((NSString *)&__NSConstantStringImpl__var_folders_03_d2zvdhkx4pg_j_03cp176fvw0000gp_T_main_6df904_mi_0, a);
            }
    
    #------------------------------------------------------------------------
    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; 
            int a = 10;
            void (*myBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
            a = 50;
            ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
    
        }
        return 0;
    }
    
    • 不难发现,我们看到了void (*myBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));这一句,就是block块里面的,是把a的值传进去了,那么,在外面修改a的值,外面修改它的值是不知道的,a的值是不会改变的!
    • 再深一层讲,block就是&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));这个家伙的地址,这个就是一个c++结构体.
    #所以,说白了,block的本质就是一个指向结构体的一个指针
    

    那么,这个结构体是什么呢?拉上去可以看到:

    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      int a;
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    • 我们需要知道的是:
      这个block块这是一个结构体
    void (*myBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
    

    对应着上面的这一段结构体(就是下面这一段)

    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {}
    
    • 传得参数都是对应的,留意a是传到_a,后面一句 a(_a)按照c++的语法,相当于把_a的值10赋值给a,那么a就是10.就是结构体里面的a就是10.

    ....................................................................

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

    接着,留意参数(__block_impl *)myBlock)->FuncPtr对应impl.FuncPtr = fp;就是fp

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

    那么,就是第一个参数的*fp,所以就相当于我们的block块的第一个参数__main_block_func_0,调了一下这个函数

    void (*myBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
    

    调用函数__main_block_func_0,来到下面这个代码块调用了这个函数

    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      int a = __cself->a; // bound by copy
    
                NSLog((NSString *)&__NSConstantStringImpl__var_folders_03_d2zvdhkx4pg_j_03cp176fvw0000gp_T_main_6df904_mi_0, a);
            }
    

    把block传进去以后访问int a = __cself->a; ,那么此时a就是10,打印出来就是10

    说白了就是值传递

    好,我们看一下下面的就改了呢?

    __block int a = 10;
    void (^myBlock)() = ^{
      NSLog(@"a = %i", a);
      };
      a = 20;
    myBlock();
    输出结果: 20
    

    下面是编译结果代码:

    struct __Block_byref_a_0 {
      void *__isa;
    __Block_byref_a_0 *__forwarding;
     int __flags;
     int __size;
     int a;
    };
    
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      __Block_byref_a_0 *a; // by ref
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      __Block_byref_a_0 *a = __cself->a; // bound by ref
    
                NSLog((NSString *)&__NSConstantStringImpl__var_folders_03_d2zvdhkx4pg_j_03cp176fvw0000gp_T_main_642b4a_mi_0,(a->__forwarding->a));
            }
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static struct __main_block_desc_0 {
      size_t reserved;
      size_t Block_size;
      void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
      void (*dispose)(struct __main_block_impl_0*);
    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
    int main(int argc, const char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
    
            __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};
            void (*block)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
            (a.__forwarding->a) = 20;
    
            ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
        }
        return 0;
    }
    
    • 我们发现,a传进去的是地址,那么结果显而易见了!

    其实就是地址传递

    总结:

    • 只有普通的定义的局部变量是值传递,其它比如是全局变量a,或者是加__block修饰的.或者是加status修饰的,都是地址传递.
    • block的本质就是一个指向结构体的指针,而编译器内会将block内部生成对应的函数,那么你就会联想到为什么block可以保存一段代码,以后再合适的位置调用了吧?!其实就是用对应的指针调用对应的函数!!!

    写在最后:

    好吧,明白了上面,相信对block有更进一步的认识了,有不对的地方大家可以交流一下...
    						 ------make by LJW 转载请注明出处-------
    
  • 相关阅读:
    Bootstrap(2)整体架构
    介绍 Microservice
    Websocket实例
    MYSQL-用户权限的验证过程(转)
    don't touch your phone in any unfamiliar way(转)
    你真的会玩SQL吗?Case的用法(转)
    android模拟器与PC的端口映射(转)
    Java Main如何被执行?(转)
    Linux crontab 命令格式与具体样例
    分享一个3D球面标签云
  • 原文地址:https://www.cnblogs.com/ljwiOS/p/5544313.html
Copyright © 2011-2022 走看看