阅读 46

iOS - Block实现原理

想必大家对block都很熟悉了,虽然都会用,但是你真的知道它的原理吗?比如为什么要加上__block,这个修饰符到底有什么用?不加会有什么后果?block又是如何实现的等等。。。该篇文章就为大家揭晓关于Block的实现原理~

#import <Foundation/Foundation.h>

void blockFunc1()
{
    int number = 100;
    void (^block)(void) = ^{
        NSLog(@"number equal %d", number);
    };
    number = 200;
    block();
}

void blockFunc2()
{
    __block int number = 100;
    void (^block)(void) = ^{
        NSLog(@"number equal %d", number);
    };
    number = 200;
    block();
}

// 全局变量
int number = 100;
void blockFunc3()
{
    void (^block)(void) = ^{
        NSLog(@"number equal %d", number);
    };
    number = 200;
    block();
}

void blockFunc4()
{
    static int number = 100;
    void (^block)(void) = ^{
        NSLog(@"number equal %d", number);
    };
    number = 200;
    block();
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        blockFunc1();
        blockFunc2();
        blockFunc3();
        blockFunc4();
    }
    return 0;
}

复制代码

截屏2021-06-04 下午1.49.57.png

疑问:

我们发现num做为局部变量时加上 _ _block 修饰符、num做为全局变量以及num为静态局部变量时在block中输出结果是一样的,皆为被修改之后的值,而做为局部变量并且未加上__block的num在block中输出的值却还是未赋值之前的值。这是为什么呢?探索这个问题我们就需要看看底层结构是如何实现的了

这里我们打开终端,cd到项目目录下,然后将用下面的命令将OC重写为C

clang -rewrite-objc main.m
复制代码

main.cpp

struct __blockFunc1_block_impl_0 {
  struct __block_impl impl;
  struct __blockFunc1_block_desc_0* Desc;
  int number;
  __blockFunc1_block_impl_0(void *fp, struct __blockFunc1_block_desc_0 *desc, int _number, int flags=0) : number(_number) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __blockFunc1_block_func_0(struct __blockFunc1_block_impl_0 *__cself) {
  int number = __cself->number; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_sn_msbs3hqn3_57wxqbtkxrgsvh0000gn_T_main_16f3b4_mi_0, number);
    }

static struct __blockFunc1_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __blockFunc1_block_desc_0_DATA = { 0, sizeof(struct __blockFunc1_block_impl_0)};
void blockFunc1()
{
    int number = 100;
    void (*block)(void) = ((void (*)())&__blockFunc1_block_impl_0((void *)__blockFunc1_block_func_0, &__blockFunc1_block_desc_0_DATA, number));
    number = 200;
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

struct __Block_byref_number_0 {
  void *__isa;
__Block_byref_number_0 *__forwarding;
 int __flags;
 int __size;
 int number;
};

struct __blockFunc2_block_impl_0 {
  struct __block_impl impl;
  struct __blockFunc2_block_desc_0* Desc;
  __Block_byref_number_0 *number; // by ref
  __blockFunc2_block_impl_0(void *fp, struct __blockFunc2_block_desc_0 *desc, __Block_byref_number_0 *_number, int flags=0) : number(_number->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __blockFunc2_block_func_0(struct __blockFunc2_block_impl_0 *__cself) {
  __Block_byref_number_0 *number = __cself->number; // bound by ref

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_sn_msbs3hqn3_57wxqbtkxrgsvh0000gn_T_main_16f3b4_mi_1, (number->__forwarding->number));
    }
static void __blockFunc2_block_copy_0(struct __blockFunc2_block_impl_0*dst, struct __blockFunc2_block_impl_0*src) {_Block_object_assign((void*)&dst->number, (void*)src->number, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __blockFunc2_block_dispose_0(struct __blockFunc2_block_impl_0*src) {_Block_object_dispose((void*)src->number, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __blockFunc2_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __blockFunc2_block_impl_0*, struct __blockFunc2_block_impl_0*);
  void (*dispose)(struct __blockFunc2_block_impl_0*);
} __blockFunc2_block_desc_0_DATA = { 0, sizeof(struct __blockFunc2_block_impl_0), __blockFunc2_block_copy_0, __blockFunc2_block_dispose_0};
void blockFunc2()
{
    __attribute__((__blocks__(byref))) __Block_byref_number_0 number = {(void*)0,(__Block_byref_number_0 *)&number, 0, sizeof(__Block_byref_number_0), 100};
    void (*block)(void) = ((void (*)())&__blockFunc2_block_impl_0((void *)__blockFunc2_block_func_0, &__blockFunc2_block_desc_0_DATA, (__Block_byref_number_0 *)&number, 570425344));
    (number.__forwarding->number) = 200;
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}


int number = 100;

struct __blockFunc3_block_impl_0 {
  struct __block_impl impl;
  struct __blockFunc3_block_desc_0* Desc;
  __blockFunc3_block_impl_0(void *fp, struct __blockFunc3_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __blockFunc3_block_func_0(struct __blockFunc3_block_impl_0 *__cself) {

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_sn_msbs3hqn3_57wxqbtkxrgsvh0000gn_T_main_16f3b4_mi_2, number);
    }

static struct __blockFunc3_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __blockFunc3_block_desc_0_DATA = { 0, sizeof(struct __blockFunc3_block_impl_0)};
void blockFunc3()
{
    void (*block)(void) = ((void (*)())&__blockFunc3_block_impl_0((void *)__blockFunc3_block_func_0, &__blockFunc3_block_desc_0_DATA));
    number = 200;
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}


struct __blockFunc4_block_impl_0 {
  struct __block_impl impl;
  struct __blockFunc4_block_desc_0* Desc;
  int *number;
  __blockFunc4_block_impl_0(void *fp, struct __blockFunc4_block_desc_0 *desc, int *_number, int flags=0) : number(_number) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __blockFunc4_block_func_0(struct __blockFunc4_block_impl_0 *__cself) {
  int *number = __cself->number; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_sn_msbs3hqn3_57wxqbtkxrgsvh0000gn_T_main_16f3b4_mi_3, (*number));
    }

static struct __blockFunc4_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __blockFunc4_block_desc_0_DATA = { 0, sizeof(struct __blockFunc4_block_impl_0)};
void blockFunc4()
{
    static int number = 100;
    void (*block)(void) = ((void (*)())&__blockFunc4_block_impl_0((void *)__blockFunc4_block_func_0, &__blockFunc4_block_desc_0_DATA, &number));
    number = 200;
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        blockFunc1();
        blockFunc2();
        blockFunc3();
        blockFunc4();
    }
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
复制代码

这里我们可以看到blockFunc1的C语言实现方法

void blockFunc1()
{
    int num = 100;
    void (*block)() = ((void (*)())&__blockFunc1_block_impl_0((void *)__blockFunc1_block_func_0, &__blockFunc1_block_desc_0_DATA, num));
    num = 200;
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
去掉类型转换

void blockFunc1()
{
    int num = 100;
    // *************************重点句***********************
    void (*block)() = &__blockFunc1_block_impl_0(__blockFunc1_block_func_0, &__blockFunc1_block_desc_0_DATA, num));
    // *****************************************************
    num = 200;
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
复制代码

这里我们可以看到

block实际上是指向结构体的指针

该结构体为

__blockFunc1_block_impl_0 我们来看下带__block的blockFunc2

blockFunc2

在 blockFunc1 中,block指向了一个名为__blockFunc1_block_impl_0的结构体,并且在初始化时输入了三个参数(__blockFunc1_block_impl_0最后的flags有默认参数,所以可以不用传参),第三个参数就是我们写的num,与blockFunc2相比较,这里的num并没有带*号,所以说在这里它只是传值而非传址,而下面的【num = 200;】也就没什么卵用了。这就是blockFunc2、blockFunc3与blockFunc4为什么能打印出num改变后的值,而blockFunc1不行的原因。

在这里我们也可以看出:

编译器会将block的内部代码生成对应的函数

** SO **

我们总结下,block在内部会作为一个指向结构体的指针,当调用block的时候其实就是根据block对应的指针找到相应的函数,进而进行调用,并传入自身

__block的实现 我们再来看看 _ block, _block也被转换成了结构体,并含有5个变量

struct __Block_byref_num_0 { void *__isa; // isa指针 __Block_byref_num_0 *__forwarding; // 实例本身 int __flags; int __size; int num; // 我们的num值 };

图片对应着blockFunc2中的

__block int num = 100; 当创建num并用__block修饰的时候,会初始化这五个变量 当我们执行

num = 200; 对应着

(num.__forwarding->num) = 200; 上面刚刚提到过 _ _forwarding是实例本身,即类型结构体__Block_byref_num_0的&num,再找到对应的num变量,将其原来的100修改为200~~

到此,关于Block内部实现的揭晓也就到此结束了,希望本文能让你对block有更深的理解,感谢你耐心的阅读!

文章分类
iOS
文章标签