zoukankan      html  css  js  c++  java
  • Blocks的实质学习总结

    更新记录

    时间 版本修改
    2020年4月12日 初稿

    前言

    • 目前看到,网上搜索到的Blocks的博客大致都是参考《Objective-C 高级编程 iOS与OS X多线程和内存管理》这本经典书籍的。
    • 本文也类似,会尽可能加入自己的总结和思考。

    Blocks的实质

    Objective-C代码转成C/C++代码

    • clang -rewrite-objc 源代码文件名

    简单的block声明

    int main(int argc, const char * argv[]) {
        void (^blk)(void) = ^{
            printf("Block
    ");
        };
        blk();
        return 0;
    }
    

    转换后的C/C++代码

    //block的实现
    struct __block_impl {
      //由于第一个成员变量是isa指针,其实是一个OC对象。本对象中记录了block函数对应的函数指针
    
      void *isa;        //block isa指针,指向类对象(stack、malloc、global三种类型之一)
      int Flags;        //某些标志,暂不深入研究
      int Reserved;     //保留区域,以后版本升级可能会用到
      void *FuncPtr;    //函数指针
    };
    
    //这就是block本身对应的结构
    struct __main_block_impl_0 {
      //只有两个成员变量。其中impl是一个OC对象
    
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;     //该block的描述
      
      //block如果捕获了一些变量,则会添加到这里
      
      //C++中可以往struct中增加构造函数
      __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;
      }
    };
    
    //静态全局函数, 函数参数就是__main_block_impl_0本身的指针。和C++和OC中调用对象成员函数类似,第一参数都是this(self)
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
         printf("Block
    ");
    }
    
    //静态全局结构体
    static struct __main_block_desc_0 {
      size_t reserved;          //保留区域,以后版本升级可能会用到
      size_t Block_size;        //本block大小
    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
    
    int main(int argc, const char * argv[]) {
        //由于括号嵌套较多,我这里采取特殊的换行和空格格式,旨在看清楚括号嵌套背后的实际语法。
    
    
        //block表达式转换
        void (*blk)(void) = (
        (void (*)())                            //强制转换成 void (*)(void) 类型的函数指针
        &                                       //取地址,记录该栈对象的地址
        __main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)     //调用struct构造函数生成该block结构体实例
        
        );
        
        //上述的blk的类型暂时转换成了 void (*)(void),但其实它应该是一个 struct __main_block_impl_0* 类型
        //由于下列的调用中还要对其进行强制转换,所以上述转成什么类型,其实无关紧要。只要把该指针记录下来就行。
        
        //block调用
        (   
            (void (*)(__block_impl *))      //将函数指针强制转换成 void (*)(__block_impl *) 类型的函数指针
            ((__block_impl *)blk)->FuncPtr  //先将 blk 强制转成 __block_impl* 类型,再取其成员变量FuncPtr。则得到一个 void * 类型的变量
        )
        ((__block_impl *)blk)   //调用参数,即将 blk 强制转成 __block_impl* 类型
        ;              
        return 0;
    }
    
    • 上述的源代码中我都插入了相关的详细注释,且通过换行和空格来去掉括号嵌套的影响,相信比较好理解。

    • 这里有一个指针强转的知识点,需要科普一下。(关于main函数中blk类型的强转)

    • 留意结构体的命名规则

      • 《OC高级编程》的作者说到:Block语法所属的函数名(此处为main)和该Block语法在该函数出现的顺序值(此处为0)来给经clang变换的函数命名。

    总结

    • 程序员声明的block,编译器会生成对应的Objective-C对象(本质就是一个结构体,由于带有isa指针,吻合Objective-C对象的定义,因此是一个Objective-C对象)。
    • 该对象种记录了block对应的函数指针,以及存储了block捕获的变量。因此后续调用block时,实质上是调用了该Objective-C对象记录的函数指针,并传递了参数(block对象本身指针self,block捕获的变量)。

    参考资料

    • 《Objective-C 高级编程 iOS与OS X多线程和内存管理》2.3.1 Block的实质
    • iOS Block实现原理
  • 相关阅读:
    PHP 内核:foreach 是如何工作的(一)
    PHP 消息队列 Kafka 使用
    PHP7 生产环境队列 Beanstalkd 正确使用姿势
    你知道Laravel ORM 中的骚操作吗
    PHP 的 interface 有什么用处
    PHP 框架 Hyperf 实现处理超时未支付订单和延时队列
    java 浅谈web系统当中的cookie和session会话机制
    如何用charles进行https抓包
    Java实现图片按修改时间排序
    java读取文本文件内容
  • 原文地址:https://www.cnblogs.com/HelloGreen/p/12684721.html
Copyright © 2011-2022 走看看