文章目录
  1. 1. __block的实现
    1. 1.1. 使用__block
    2. 1.2. 转化分析
    3. 1.3. 总结

__block的实现

  • 大家好,我叫LastDays,这是我的Blog,我在这里分享我的学习,
  • 我的微博我在这里分享我的生活,欢迎交流

使用__block

看一下这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {

int x = 1;

// insert code here..
void (^blk)(void) = ^{
printf("%d\n",x);
};
x = 2;
blk();
}
return 0;
}

运行结果为:1

疑惑的问题可能是为什么没有输出2?

其实原因很简单,block的isa指向该block的Class。当struct第一次被创建时,它是存在于该函数的栈帧上的,其Class是固定的_NSConcreteStackBlock。其捕获的变量是会赋值到结构体的成员上,所以当block初始化完成后,捕获到的变量不能更改。

如果我们希望在block语法中更改外部的自动变量x,这个时候我们需要使用__block说明符,就像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {

__block int x = 1;

// insert code here..
void (^blk)(void) = ^{
x = 2;
printf("%d\n",x);
};

blk();
}
return 0;
}

转化分析

接下来就是讨论__block的实现原理,讲下面的代码转化为C的代码:

1
clang -rewrite-objc main.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {

__block int x = 1;

// insert code here..
void (^blk)(void) = ^{
x = 2;
printf("%d\n",x);
};

blk();
}
return 0;
}

转化后代码:

1
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __Block_byref_x_0 {
  void *__isa;
__Block_byref_x_0 *__forwarding;
 int __flags;
 int __size;
 int x;
};

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

            (x->__forwarding->x) = 2;
            printf("%d\n",(x->__forwarding->x));
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->x, (void*)src->x, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->x, 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[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        __attribute__((__blocks__(byref))) __Block_byref_x_0 x = {(void*)0,(__Block_byref_x_0 *)&x, 0, sizeof(__Block_byref_x_0), 1};


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

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

在上面的代码中,我们并不能找到我们定义的x变量,但是却找到了一个新的结构体:

1
2
3
4
5
6
7
struct __Block_byref_x_0 {
void *__isa;
__Block_byref_x_0 *__forwarding;
int __flags;
int __size;
int x;
};

在这里我们找到了我们的声明的变量x,还记得iOS内存管理之Blocks的实现(二),分析得出,block被转化为了一个c函数,然后被调用。

看一下我们的main函数

1
__block int x = 1;

对应转化后的代码为:

1
__attribute__((__blocks__(byref))) __Block_byref_x_0 x = {(void*)0,(__Block_byref_x_0 *)&x, 0, sizeof(__Block_byref_x_0), 1};

在这里我们基本上也就能看出来了,其实,这根我们的block的实现原理差不多,block转变为Block_byref_x_0结构体实例,那么这个实例中,也就拥有x成员变量。

接着看一下

1
2
3
4
void (^blk)(void) = ^{
x = 2;
printf("%d\n",x);
};

转化后对应的代码:

1
2
3
4
5
6
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_x_0 *x = __cself->x; // bound by ref

(x->__forwarding->x) = 2;
printf("%d\n",(x->__forwarding->x));
}

_ Block byref_ x_0结构体中我们能发现,这个struct的首地址同样为isa,并且我们可以简化一下*_ Block byref_ x_0结构体的初始化

1
2
3
4
5
6
7
__Block_byref_x_0 x = {
0,
&x,
0,
sizeof(__Block_byref_x_0),
1
};

可以看见,_ Block byref_ x_0结构体成员中的__forwarding拥有指向该实例自身的指针,并且这里的x就相当于以下代码中的x:

1
2
3
4
void (^blk)(void) = ^{
x = 2;
printf("%d\n",x);
};

总结

OK那么我们也就基本上分析出来了block的原理,其实跟block差不多,两者都拥有自己的结构体,并且是现实的时候都进行初始话处理。其实也就相当于main初始化调用 main block_ impl0结构体, main block impl_0结构体初始化调用Block byref x 0 结构体和 __ block impl结构体。 人后分别通过__forwarding与FuncPtr调用响应的东西。

PS:为什么会有forwarding,接下来通过block和block的存储域分析。有什么问题,或者讨论,欢迎留言。

文章目录
  1. 1. __block的实现
    1. 1.1. 使用__block
    2. 1.2. 转化分析
    3. 1.3. 总结