美文网首页
Block实现原理

Block实现原理

作者: WSJay | 来源:发表于2019-01-02 15:13 被阅读0次
  • Block是带有自动变量值的匿名函数;
  • 带有自动变量值在Block中表现为截获自动变量值
  • 自动变量值截获只能保存执行Block语法瞬间的值,保存后就不能改写该值;
  • 向截获的自动变量赋值会产生编译错误,但使用截获的值不会产生任何问题;
  • 使用__block说明符的自动变量可在Block中赋值,该变量称为__block变量;
  • Block其实就是OC对象;

一、Block的实质

使用 clang -rewrite-objc main.m将包含Block语法的源代码转换成C++的源代码。


#import <Foundation/Foundation.h>

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

转换后的C++源码:

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr; //指向Block的实现
};

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("-------------Block------------------\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(int argc, const char * argv[]) {

    void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));

    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    return 0;
}

源码中的Block语法:

^{ printf("-------------Block------------------\n"); };

变换后的源代码中也含有相同的表达式:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

       printf("-------------Block------------------\n");
}

参数__cself是指向__main_block_impl_0结构体的指针变量。

struct __main_block_impl_0 *__cself;

该结构体的声明如下:

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
};

第一个成员变量是impl,其结构体如下:

struct __block_impl {
  void *isa;
  int Flags; // 标志
  int Reserved; // 预留区
  void *FuncPtr; // 函数指针
};

第二个成员变量是Desc指针,其__main_block_desc_0结构体如下:

static struct __main_block_desc_0 {
  size_t reserved; // 预留区
  size_t Block_size; // Block大小
} ;

下面是包含这些结构体的__main_block_impl_0结构体的构造函数:

__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;
  }

下面是构造函数的调用:


void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));

将上面的转换部分删除之后,具体如下:

struct __main_block_impl_0 temp = __main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
struct __main_block_impl_0 *blk = &temp;

Block的调用转化为以下源码:

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

将上面的转换部分删除之后,具体如下:

 (*blk->impl.FuncPtr)(blk);

这就是简单的使用函数指针调用函数。由Block语法转换的__main_block_func_0函数的指针被赋值成员变量FuncPtr中。此外,__main_block_func_0函数的参数
__cself指向Block值。在调用该函数的源码中可以看出Block正是作为参数进行了传递。
成员变量isa的初始化如下:

 impl.isa = &_NSConcreteStackBlock;

_NSConcreteStackBlock相当于class_t结构体实例。将Block作为OC对象处理时,关于该类的信息位于_NSConcreteStackBlock中。
Block的实质就是Block为Objective-C对象。

二、捕获自动变量值

如下示例:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    
    int dmy = 250;
    int val = 10;
    const char * fmt = "val = %d \n";
    
    void (^blk)(void) = ^{
        printf(fmt,val);
    };

    blk();
    return 0;
}

Block捕获局部变量valfmt的值,转换之后的源码如下:


struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  const char *fmt;
  int val;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags=0) : fmt(_fmt), val(_val) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  const char *fmt = __cself->fmt; // bound by copy
  int val = __cself->val; // bound by copy

        printf(fmt,val);
    }

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[]) {

    int dmy = 250;
    int val = 10;
    const char * fmt = "val = %d \n";

    void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, fmt, val));

    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    return 0;
}

由上面的源码可知,Block语法表达式中使用的自动变量被作为成员变量追加到__main_block_impl_0结构体中。而没有使用的自动变量不会被追加。

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  const char *fmt;
  int val;
};

下面来看Block匿名函数的实现,初始源码如下:

   void (^blk)(void) = ^{
        printf(fmt,val);
    };

该源码转换为以下函数:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  const char *fmt = __cself->fmt; // bound by copy
  int val = __cself->val; // bound by copy
        printf(fmt,val);
}

由上可知:所谓截获自动变量值意味在执行Block语法时,Block语法表达式所使用的自动变量值被保存到Block的结构体实例(即Block自身)中。

三、全局变量、全局静态变量和局部静态变量

#import <Foundation/Foundation.h>

// 全局变量
int global_val = 1990; 
// 全局静态变量
static int static_global_val = 1011;

int main(int argc, const char * argv[]) {
   
     // 局部静态变量
    static int static_val = 1994;
    
    void (^blk)(void) = ^{
        global_val *= 2;
        static_global_val *= 3;
        static_val *= 4;
    };

    blk();
    return 0;
}

在上面的示例中,定义了三个变量,一个全局变量global_val,一个静态全局变量 static_global_val,一个局部静态变量static_val,然后在Block语法中修改这三个变量的值。该源码转换后如下:

int global_val = 1990;
static int static_global_val = 1011;

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int *static_val;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_val, int flags=0) : static_val(_static_val) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int *static_val = __cself->static_val; // bound by copy

        global_val *= 2;
        static_global_val *= 3;
        (*static_val) *= 4;
    }

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[]) {

    static int static_val = 1994;

    void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &static_val));

    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    return 0;
}

对全局变量global_val和静态全局变量 static_global_val的访问和转换前完全相同。而对局部静态变量static_val的访问转换如下:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int *static_val = __cself->static_val; // bound by copy
  (*static_val) *= 4;
}

使用局部静态变量static_val的指针对其进行访问,将static_val的指针传递给__main_block_impl_0结构体的构造函数并保存。

四、__block存储域类说明符

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
   
    __block int val = 1994;
    
    void (^blk)(void) = ^{
        val += 25;
    };
    
    blk();
    return 0;
}

struct __Block_byref_val_0 {
 void *__isa;
__Block_byref_val_0 *__forwarding;
int __flags;
int __size;
int val;
};

struct __main_block_impl_0 {
 struct __block_impl impl;
 struct __main_block_desc_0* Desc;
 __Block_byref_val_0 *val; // by ref
 __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_val_0 *_val, int flags=0) : val(_val->__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_val_0 *val = __cself->val; // bound by ref

       (val->__forwarding->val) += 25;
}

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->val, (void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->val, 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[]) {

   __attribute__((__blocks__(byref))) __Block_byref_val_0 val = {(void*)0,(__Block_byref_val_0 *)&val, 0, sizeof(__Block_byref_val_0), 1994};

   void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_val_0 *)&val, 570425344));

   ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
   return 0;
}

由上面的源码可知,__block变量被转换成__Block_byref_val_0结构体类型的自动变量,即栈上生成的__Block_byref_val_0结构体实例。该结构体声明如下:

struct __Block_byref_val_0 {
 void *__isa;
__Block_byref_val_0 *__forwarding;
int __flags;
int __size;
int val;
};

下面是给__block变量赋值的源码转换:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
 __Block_byref_val_0 *val = __cself->val; // bound by ref

       (val->__forwarding->val) += 25;
}

在给Block中的局部静态变量赋值时,使用了指向该静态变量的指针。而向 __block变量赋值则更复杂。Block的__main_block_impl_0结构体实例持有指向 __block变量的__Block_byref_val_0结构体实例的指针。
__Block_byref_val_0结构体实例的成员变量__forwarding持有指向该实例自身的指针。通过__forwarding访问成员变量val,可以保证 __block变量无论配置在栈上还是堆上都可以正确的访问__block变量。这是因为 __block变量的实例在栈上时,__forwarding是指向自身的指针,当 __block变量从栈上拷贝到堆上时,__forwarding将指向复制到堆上的__block变量实例。
__block变量的__Block_byref_val_0结构体并不在Block的__main_block_impl_0结构体中,是为了在多个Block中使用 __block变量。

相关文章

  • iOS-2 Block

    block块 系列文章: iOS Block浅浅析 - 简书 iOS Block实现原理 iOS Block __...

  • iOS Block存储域及循环引用

    系列文章:iOS Block概念、语法及基本使用iOS Block实现原理iOS Block __block说明符...

  • iOS Block 部分一

    主要讲解 Block 的底层实现原理; Block部分一Block部分二Block部分三Block知识点总结 基础...

  • Today面试

    Runloop 底层原理Kvo 底层原理ARC 底层原理 如何实现GCD 底层原理Block 底层原理Aut...

  • iOS Block概念、语法及基本使用

    系列文章:iOS Block实现原理iOS Block __block说明符 最近又翻了一遍《Objective-...

  • 深入研究Block用weakSelf、strongSelf、@w

    前言 在上篇中,仔细分析了一下Block的实现原理以及__block捕获外部变量的原理。然而实际使用Block过程...

  • iOS Block浅析

    Block实现原理 要想知道Block的内部实现,需要知道Block编译完后是什么样子,使用clang可看到Blo...

  • Block

    Block 1.Block的定义和语法2.Block的本质和分类3.__block的实现原理 Block的定义和语...

  • Block实现原理

    Block是带有自动变量值的匿名函数; 带有自动变量值在Block中表现为截获自动变量值; 自动变量值截获只能保存...

  • Block实现原理

    摘要 引用自http://blog.itanbing.com/2015/07/20/block-retain-cy...

网友评论

      本文标题:Block实现原理

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