美文网首页
Blcok 源码分析1 block的执行

Blcok 源码分析1 block的执行

作者: 大墙66370 | 来源:发表于2019-01-23 16:01 被阅读5次

    日常开发中block的用处可以说是无处不在,也许你已经能够很熟练的运用block.也许你已经很有了足够多使用block的经验可以轻而易举的避免block背后的风险(比如在调用block的时候 判断block是否为nil 在调用block的时候是否会循环引用....) .但是你知道block的本质是什么吗?执行block() 为啥会回调前面的^{ // }; 代码快中呢? 如果不清楚那么我们带着这些疑问一步一步来操作,一行一行代码来解释.(清楚的请忽略此文章)

    我大概会分这么几部去分析

    1先一个简单的工程

    在main.m的main函数中实现一个很简单的block

    //  main.m
    //  block1
    //
    //  Created by lcc on 2019/1/23.
    //  Copyright © 2019年 lcc. All rights reserved.
    //
    
    #include <stdio.h>
    
    int main(int argc, char * argv[]) {
        void(^lccBlock)(void) = ^{
               printf("--");
        };
    
        lccBlock();
    }
    
    2用Clang吧main.m转化成main.cpp代码
    如图 WechatIMG218.png
    3用分析上图生成的main.cpp代码

    短短的一个几行代码当我们转成.cpp代码的时候 你会发现变成了很多,我们挑出有用的代码复制到main.m中因为包含c++代码所以.m后缀我们改成.mm后缀. 并逐行分析没行代码

    //
    //  main.m
    //  block1
    //
    //  Created by lcc on 2019/1/23.
    //  Copyright © 2019年 lcc. All rights reserved.
    //
    
    #include <stdio.h>
    
    #define __OBJC_RW_DLLIMPORT extern
    
    #ifdef __OBJC_EXPORT_BLOCKS
    extern "C" __declspec(dllexport) void _Block_object_assign(void *, const void *, const int);
    extern "C" __declspec(dllexport) void _Block_object_dispose(const void *, const int);
    extern "C" __declspec(dllexport) void *_NSConcreteGlobalBlock[32];
    extern "C" __declspec(dllexport) void *_NSConcreteStackBlock[32];
    #else
    __OBJC_RW_DLLIMPORT void _Block_object_assign(void *, const void *, const int);
    __OBJC_RW_DLLIMPORT void _Block_object_dispose(const void *, const int);
    __OBJC_RW_DLLIMPORT void *_NSConcreteGlobalBlock[32];
    __OBJC_RW_DLLIMPORT void *_NSConcreteStackBlock[32];
    #endif
    
    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;
     __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;
     }
    };
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
     
     printf("--");
    }
    
    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, char * argv[]) {
     //1 下面是源码 但是这里是报错的.没关系我们先注释掉 我们先分析下 这行代码
    //    void(*lccBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
     /*
      等号左边 void(*lccBlock)(void) lccBlock就是一个指针 这个指针是什么类型的呢 他的类型可以void(*)(void)这样表示 就是一个没有返回值 没有参数的函数指针
      等号右边 ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)) 比较复杂我们先一点一点分析
      首先我们先看一下 __main_block_impl_0这个结构体 这个是c++的结构体 __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0){} 就是这个结构体的构造函数  (void (*)()) 就是类型强转 等号左边是一个指针 右边肯定也得是指针 等号右边就是一个结构体的实例的地址 被强转成了(void (*)())类型 我们可以拆分一下
      */
     
     //生成一个结构体实例 我们看__main_block_impl_0()这个构造函数接受的参数 第一个是void *fp 任何类型的指针 我们传了一个__main_block_func_0函数的指针 第二个参数 __main_block_desc_0结构体指针 __main_block_desc_0_DATA就是 __main_block_desc_0类型的一个实例 我们穿的第二个参数就是 __main_block_desc_0_DATA这个实例的地址没问题 第三个参数是默认参数外界不用传.
     struct __main_block_impl_0 __main_block_impl_0_instance = __main_block_impl_0((void *)__main_block_func_0,&__main_block_desc_0_DATA);
     
     //这里 lccBlock 指针指向 NULL
     void(*lccBlock)(void) = NULL;
     //__main_block_impl_0 类型的指针
     struct __main_block_impl_0 *p = &__main_block_impl_0_instance;
     //强制类型转换后赋值给 lccBlock lccBlock说白了 就只一个结构体指针而已
     lccBlock = (void (*)())p;
     //简化结果就是下面
    //    void(*lccBlock)(void) = (void (*)())&__main_block_impl_0_instance;
     
     //我们打开下面注释 分析一下
    //    ((void (*)(__block_impl *))((__block_impl *)lccBlock)->FuncPtr)((__block_impl *)lccBlock);
     
     ((void (*)(__block_impl *))((__block_impl *)lccBlock)->FuncPtr)((__block_impl *)lccBlock);
     //((void (*)(__block_impl *))((__block_impl *)lccBlock)->FuncPtr)就是函数名 ((__block_impl *)lccBlock)这个括号里面(__block_impl *)lccBlock就是 函数的参数
     //这个函数 其实就是 FuncPtr 函数 我们分析上面 __main_block_impl_0结构体 也就是是 __main_block_impl_0_instance实例里面impl结构体实例的一个 FuncPtr
     // 步骤1((__block_impl *)lccBlock) 这里是吧 lccBlock指针类型 强转成了 __block_impl类型的指针
     // 步骤2((__block_impl *)lccBlock)->FuncPtr) 这里其实就是 p->impl.FuncPtr 这里我就有个疑问了 就算指针lccBlock的类型可以强转换 但是真正指向的地址是不变的啊 lccBlock 一直都是指向的 __main_block_impl_0_instance这个结构体的地址啊
     // 步骤3(void (*)(__block_impl *)) 强转的类型 说面吧 ((__block_impl *)lccBlock)->FuncPtr 这个指针强转成了 (void (*)(__block_impl *))的函数指针 这个函数的参数是__block_impl *指针  所以步骤1才会把 lccBlock指针类型 强转成了 __block_impl类型的指针
     
     //我们这样也可以达到和上面 相同的效果
     ((void(*)(__main_block_impl_0 *))(p->impl.FuncPtr))(p);
     
    }
    
    //int main(int argc, char * argv[]) {
    //    void(^lccBlock)(void) = ^{
    //        printf("--");
    //    };
    //
    //    lccBlock();
    //}
    

    以后会分析 带参数的block 待返回值的block block如何捕获变量 __block的原理

    相关文章

      网友评论

          本文标题:Blcok 源码分析1 block的执行

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