iOS - block原理解读(三)

前言

在阅读该篇文章前,推荐阅读
ios - block原理解读(一)
ios - block原理解读(二)

本文解决问题

  • __block原理
  • copy函数,从栈copy到堆的过程(同对象变量)

__block原理

通过__block修饰的自动变量,可以在block内部进行值修改。

typedef void (^Block)(void);
Block block;

int main(int argc, char * argv[]) {
    @autoreleasepool {
        
        __block int a = 10;
        
        block = ^{
            a++;
        };
        
        return 0;
    }
}

将代码编译成C++源码

// 原代码
__block int a = 10;
// c++源码
__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {
    (void*)0,
    (__Block_byref_a_0 *)&a, 
    0, 
    sizeof(__Block_byref_a_0), 
    10
};

可以看到 变量a 变成了 结构体类型__Block_byref_a_0

下面再看看结构体__Block_byref_a_0的构造

struct __Block_byref_a_0 {
  void *__isa;
__Block_byref_a_0 *__forwarding;
 int __flags;
 int __size;
 int a;
};

通过上面结构体的初始化和结构体的构造,
可以获得以下信息:

  1. __forwarding存放的是自己本身的地址
  2. 结构体内的a变量存放的是外部变量a的值

主结构体__main_block_impl_0的变化

image.png

image.png

首先说明一点,
在block初始化过程中,有一个由栈block指向堆block的过程。

一开始,栈空间的block有一个__Block_byref_a_0结构体,
指向外部__Block_byref_a_0的地址,
其中它的__forwarding指针指向自身,

当block从栈copy到堆时,

堆空间的block有一个__Block_byref_a_0结构体,
指向外部__Block_byref_a_0的地址,
其中它的__forwarding指针指向自身(复读机)

如何从栈指向堆,并建立联系呢?

apple源码,如图:


copy函数相关源码.png

copy->forwarding = copy;
就是将堆结构体的__forwarding指针指向自身
src->forwarding = copy;
就是将栈结构体的__forwarding指针指向堆结构体

这样,苹果工程师在背后悄悄地将block copy到了堆上,
而且栈上的block从未被我们利用过。

在看看block入口静态函数

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_a_0 *a = __cself->a; // bound by ref
  (a->__forwarding->a)++;
}

通过当前栈空间主结构体上的__Block_byref_a_0结构体指针,访问指向堆空间的__forwarding成员,并获取堆空间上变量的值。

当然,不仅__block修饰的变量会这样,前文的对象类型变量同样会在copy函数内部被转化成类似的结构体进行处理。

你可能感兴趣的:(iOS - block原理解读(三))