阅读 183

iOS Block底层分析

学习重点

Block底层结构

Block类型

Block底层源码探究

1. Block捕获变量

  创建iOS工程,在自定义的BlockTestViewController控制器中编写如下代码:

//BlockTestViewController.m文件
#import "BlockTestViewController.h"
#import "Person.h"

@interface BlockTestViewController ()

@property (nonatomic, strong) Person *person;

@end

@implementation BlockTestViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    [self blockTest];
}

- (void)blockTest {
    //第一组 捕获普通局部变量
    int a = 18;
    void(^block_a)(void) = ^{

        printf("a - %d",a);
    };
    
    block_a();

    //第二组 捕获__block修饰的局部变量
    __block int b = 20;
    void(^block_b)(void) = ^{
        b++;
        printf("b - %d",b);
    };
    
    block_b();
    
    //第三组 捕获OC对象
    Person *person1 = [[Person alloc] init];
    
    void(^block_p)(void) = ^{
        person1.name = @"222";
        NSLog(@"person.name = %@", person1.name);
    };
    
    block_p();
    
    //第四组 捕获__block修饰的OC对象
    __block Person *person2 = [[Person alloc] init];
    
    void(^block_p2)(void) = ^{
        person2.name = @"333";
        NSLog(@"person.name = %@", person2.name);
    };
    
    block_p2();
    
    //第五组 捕获__weak修饰的OC对象
    __weak typeof(self) weakSelf = self;
    
    void(^block_weak)(void) = ^{
        weakSelf.person.name = @"444";
        NSLog(@"person.name = %@", weakSelf.person.name);
    };
    
    block_weak();
    
    //第六组 捕获block
    void(^block_block)(void) = ^{
        block_weak();
    };
    
    block_block();
}

- (Person *)person {
    if (!_person) {
        _person = [[Person alloc] init];
    }
    
    return _person;
}

- (void)dealloc
{
    NSLog(@"销毁了!!!");
}
@end


//Person.h文件
@interface Person : NSObject

@property (nonatomic, copy) NSString *name;

@end


//Person.m文件
#import "Person.h"

@implementation Person

@end
复制代码

  为了研究Block底层结构,首先我们打开终端使用xcrun命令将BlockTestViewController.m文件编译为cpp文件,如下所示: 命令为:

xcrun -sdk iphones \
> clang -arch arm64 \
> -rewrite-objc \
> -fobjc-arc \
> -fobjc-runtime=macosx-10.14 \
> -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.5.sdk \
> BlockTestViewController.m
复制代码

  对上述编译好的cpp代码逐个进行分析,你会发现某些结构体中嵌套了相同的结构体__block_impl以及其他数据结构变量,并且使用了一些外部函数,如下所示:

//共有结构
extern "C" __declspec(dllexport) void *_NSConcreteStackBlock[32];

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

//外部函数
extern "C" __declspec(dllexport) void _Block_object_assign(void *, const void *, const int);
extern "C" __declspec(dllexport) void _Block_object_dispose(const void *, const int);
复制代码

&emsp: 如果你不熟悉C++语言,看着以上的代码可能就很费劲了,所以在接下来的探究过程中,会对以上编译完成的cpp代码进行简写(例如:__BlockTestViewController__blockTest_block_impl_0会写成_block_impl_0,可以发现的是这些结构体的命名都是有规范的,会将其定义位置(__所在文件名__所在函数名)作为名字前缀),

1.1 Block捕获局部变量

  第一组block生成的相关cpp代码如下所示:

static struct _block_desc_0 {
  size_t reserved;
  size_t Block_size;
} _block_desc_0_DATA = { 0, sizeof(struct _block_impl_0)};


struct _block_impl_0 {
  struct __block_impl impl;
  struct _block_desc_0* Desc;
  int a;
  //_block_impl_0结构体中自定义的构造函数
  _block_impl_0(void *fp, struct _block_desc_0 *desc, int _a, int flags=0) : a(_a) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static void _block_func_0(struct _block_impl_0 *__cself) {

    int a = __cself->a; // bound by copy

    printf("a - %d",a);
}

复制代码

  blockTest方法中block_a编译的相关cpp代码如下所示(为了能让你能更容易看懂,我将其中的代码做了修改,但是并不影响执行结果):

    int a = 18;
    
    void(*block_a)(void);
    
    //调用_block_impl_0构造函数初始化block_impl_0变量,_block_func_0函数指针被强制转换为(void *)空指针作为参数传入,_block_desc_0_DATA实际上是定义的_block_desc_0结构体类型的全局变量
    struct _block_impl_0 block_impl_0{ (void *) _block_func_0, &_block_desc_0_DATA, a};
    
    //获取指向block_impl_0变量的指针,并将这个指针强转为 (void (*)()) 类型函数指针。
    block_a = ( void (*)() ) &block_impl_0;
    
    //再将这个block_a强转为__block_impl结构体指针,获取其成员变量FuncPtr并将这个函数强转为 (void (*)(struct __block_impl *) 类型函数指针,然后进行调用,最后block_a强转为__block_impl结构体指针作为参数传入。
    ( (void (*)(struct __block_impl *) ) ((struct __block_impl *)block_a)->FuncPtr)( (__block_impl *) block_a);
复制代码

  分析上述代码,可以发现我们在OC中所定义的Block类型变量block_a,在编译器编译后,block_a中定义的代码块会编译为一个特有的函数_block_func_0,还会生成一种结构体类型_block_impl_0,而block_a实际上就是这种结构体类型变量,这种结构体类型包含了__block_impl结构体中所有的成员变量,而block_a结构体变量在初始化时(isa会被初始化为指向_NSConcreteStackBlock指针,flags初始化为0FuncPtr会初始化为代码块函数指针_block_func_0),对基本数据类型的局部变量a进行捕获时,只是对这个局部变量的值进行了拷贝,将其值拷贝到与局部变量a类型一致的成员变量a中,然后在OC中执行Block中的代码块时,在底层实际上是对所生成的block_a结构体变量中成员变量FuncPtr这个函数指针的调用,并传入指向block_a结构体变量的指针作为参数传入,以便在_block_func_0函数中,可以对捕获的局部变量a的值进行使用。

1.2 Block捕获使用__block修饰的局部变量

  第二组block生成的相关cpp代码如下所示:

static void _block_copy_1(struct _block_impl_1*dst, struct _block_impl_1*src) {
    _Block_object_assign((void*)&dst->b, (void*)src->b, 8/*BLOCK_FIELD_IS_BYREF*/);
}


static void _block_dispose_1(struct _block_impl_1*src) {
    _Block_object_dispose((void*)src->b, 8/*BLOCK_FIELD_IS_BYREF*/);
}


static struct _block_desc_1 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct _block_impl_1*, struct _block_impl_1*);
  void (*dispose)(struct _block_impl_1*);
} _block_desc_1_DATA = { 0, sizeof(struct _block_impl_1), _block_copy_1, _block_dispose_1};


struct __Block_byref_b_0 {
  void *__isa;
  __Block_byref_b_0 *__forwarding;
  int __flags;
  int __size;
  int b;
};


struct _block_impl_1 {
  struct __block_impl impl;
  struct _block_desc_1* Desc;
  __Block_byref_b_0 *b; // by ref
  _block_impl_1(void *fp, struct _block_desc_1 *desc, __Block_byref_b_0 *_b, int flags=0) : b(_b->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};


static void _block_func_1(struct _block_impl_1 *__cself) {

    __Block_byref_b_0 *b = __cself->b; // bound by ref

    (b->__forwarding->b)++;
    
    printf("b - %d",(b->__forwarding->b));
}
复制代码

  blockTest方法中block_b编译的相关cpp代码如下所示:

    ((void (*)(__block_impl *))((__block_impl *)block_b)->FuncPtr)((__block_impl *)block_b);
    
       __attribute__((__blocks__(byref))) struct __Block_byref_b_0 b = { (void *) 0, (__Block_byref_b_0 *) &b, 0, sizeof(__Block_byref_b_0), 20};
    
    void(*block_b)(void);
    
    struct _block_impl_1 block_impl_1{(void *)_block_func_1, &_block_desc_1_DATA, (__Block_byref_b_0 *) &b, 570425344};
    
    block_b = (void (*)(void))&block_impl_1;
    
    ( (void(*)(struct __block_impl *) ) ( ( (struct __block_impl *) block_b)->FuncPtr ) ) ( (struct __block_impl *) block_b);
复制代码

  经过分析可以发现,block_b定义以及使用与block_a是很相似的,但是有两个地方是不同的:

  • 第1处:捕获变量的类型,可以发现当使用__block修饰了局部变量b时,当编译器编译到这行代码时,会定义__Block_byref_b_0这个结构体类型,并且b不再是一个普通的数据类型变量,而是变成了__Block_byref_b_0结构体变量,对这个变量初始化时,成员变量isa赋值为((void *) 0NULL,成员变量__forwarding赋值为指向b的指针,成员变量__flags赋值为0,成员变量__size赋值为自身结构体大小,成员变量b被初始化为20(也就是原来OC中定义的局部变量b的值),在__Block_byref_b_0结构体变量b初始化完后,将其地址指针赋值给了block_impl_1的成员变量(__Block_byref_b_0 *类型)b

  • 第2处block_b成员变量Desc指针类型_block_desc_1中多了两个函数指针类型成员变量copy以及dispose,而全局变量_block_desc_1_DATA初始化时,这两个成员变量的值分别为_block_copy_1以及_block_dispose_1函数指针。

  回到OC代码中,局部变量b前之所以要使用__block修饰,是因为我们在block_b闭包的代码块中修改了b的值,就需要捕获b这个局部变量的地址指针,而使用__block修饰基本数据类型局部变量的作用,就是在编译器编译阶段会生成一个包含b这种基本数据类型成员变量b的结构体__Block_byref_b_0,并创建__Block_byref_b_0结构体类型的变量b,然后在闭包结构体变量block_b中也会定义一个__Block_byref_b_0结构体指针b,初始化block_b这个结构体变量的时候,会传入__Block_byref_b_0结构体变量b的地址,这样在闭包代码块中就可以修改以及访问其成员变量b指针所指向内存空间b的数据了(你可以看做编译器是将使用__block修饰的局部变量b包装成了一个OC对象,因为__Block_byref_b_0中有isa存在)。

1.3 Block捕获OC对象

  第三组block生成的相关cpp代码如下所示:

static void _block_copy_2(struct _block_impl_2*dst, struct _block_impl_2*src) {

    _Block_object_assign((void*)&dst->person1, (void*)src->person1, 3/*BLOCK_FIELD_IS_OBJECT*/);
    
}


static void _block_dispose_2(struct _block_impl_2*src) {

    _Block_object_dispose((void*)src->person1, 3/*BLOCK_FIELD_IS_OBJECT*/);
    
}


static struct _block_desc_2 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct _block_impl_2*, struct _block_impl_2*);
  void (*dispose)(struct _block_impl_2*);
} _block_desc_2_DATA = { 0, sizeof(struct _block_impl_2), _block_copy_2, _block_dispose_2};


struct _block_impl_2 {
  struct __block_impl impl;
  struct _block_desc_2* Desc;
  Person *__strong person1;
  _block_impl_2(void *fp, struct _block_desc_2 *desc, Person *__strong _person1, int flags=0) : person1(_person1) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};


static void _block_func_2(struct _block_impl_2 *__cself) {
  Person *__strong person1 = __cself->person1; // bound by copy

        ((void (*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)person1, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_99_49qsqpv90l58q7813rrhltjc0000gn_T_BlockTestViewController_8bb5c3_mi_0);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_99_49qsqpv90l58q7813rrhltjc0000gn_T_BlockTestViewController_8bb5c3_mi_1, ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)person1, sel_registerName("name")));
    }
复制代码

  blockTest方法中block_p编译的相关cpp代码如下所示:

//将objc_msgSend强转为空指针,然后再强转为(Person * (*) (id, SEL))类型函数指针,进行调用...
Person *person1 = ( (Person * (*) (id, SEL) ) (void *) objc_msgSend ) ((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));

    void(*block_p)(void);
    
    struct _block_impl_2 block_impl_2{(void *)_block_func_2, &_block_desc_2_DATA, person1, 570425344};
    
    block_p = (void (*)(void))&block_impl_2;

    ((void (*)(__block_impl *))((__block_impl *)block_p)->FuncPtr)((__block_impl *)block_p);
复制代码

  通过分析可知,当闭包中捕获的是OC对象时,不会像第二组Block似的在编译阶段生成一个__Block_byref_p_0这种结构体类型,但是会在定义全局变量_block_desc_2_DATA时传入相应的copy以及dispose的函数指针。

1.4 Block捕获使用__block修饰的OC对象

  第四组block生成的相关cpp代码如下所示:

static void _block_copy_3(struct _block_impl_3*dst, struct _block_impl_3*src) {

    _Block_object_assign((void*)&dst->person2, (void*)src->person2, 8/*BLOCK_FIELD_IS_BYREF*/);
    
}


static void _block_dispose_3(struct _block_impl_3*src) {_Block_object_dispose((void*)src->person2, 8/*BLOCK_FIELD_IS_BYREF*/);}


static struct _block_desc_3 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct _block_impl_3*, struct _block_impl_3*);
  void (*dispose)(struct _block_impl_3*);
} _block_desc_3_DATA = { 0, sizeof(struct _block_impl_3), _block_copy_3, _block_dispose_3};


struct __Block_byref_person2_1 {
  void *__isa;
__Block_byref_person2_1 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 Person *__strong person2;
};


struct _block_impl_3 {
  struct __block_impl impl;
  struct _block_desc_3* Desc;
  __Block_byref_person2_1 *person2; // by ref
  _block_impl_3(void *fp, struct _block_desc_3 *desc, __Block_byref_person2_1 *_person2, int flags=0) : person2(_person2->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};


static void __Block_byref_id_object_copy_131(void *dst, void *src) {

    _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
    
}


static void __Block_byref_id_object_dispose_131(void *src) {

    _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
    
}

static void _block_func_3(struct _block_impl_3 *__cself) {
    __Block_byref_person2_1 *person2 = __cself->person2; // bound by ref

    ((void (*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)(person2->__forwarding->person2), sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_99_49qsqpv90l58q7813rrhltjc0000gn_T_BlockTestViewController_8bb5c3_mi_2);
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_99_49qsqpv90l58q7813rrhltjc0000gn_T_BlockTestViewController_8bb5c3_mi_3, ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)(person2->__forwarding->person2), sel_registerName("name")));
        
}

复制代码

  blockTest方法中block_p2编译的相关cpp代码如下所示:

 __attribute__((__blocks__(byref))) __Block_byref_person2_1 person2 = {(void*)0,(__Block_byref_person2_1 *)&person2, 33554432, sizeof(__Block_byref_person2_1), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"))};
 
    void(*block_p2)(void);
    
    struct _block_impl_3 block_impl_3{(void *)_block_func_3, &_block_desc_3_DATA, person2, 570425344};

    block_p2 = (void (*)(void))&block_impl_3;

    ((void (*)(__block_impl *))((__block_impl *)block_p2)->FuncPtr)((__block_impl *)block_p2);
复制代码

  通过分析可以发现,Block捕获使用__block修饰的OC对象,跟第二组Block情况类似,只不过第二组Block是将一个基本数据类型b存储到__Block_byref_b_0结构体类型变量中的成员变量b中,然后将__Block_byref_b_0类型变量指针保存到_block_impl_1结构体变量block_impl_1的成员变量b中,而本组是将一个OC对象存储到结构体__Block_byref_person2_1中的成员变量person2中,然后将__Block_byref_person2_1类型变量指针保存到_block_impl_3结构体变量block_impl_3的成员变量person2中。

1.5 Block捕获使用__weak修饰的OC对象

  第五组block生成的相关cpp代码如下所示:

static struct _block_desc_4 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct _block_impl_4*, struct _block_impl_4*);
  void (*dispose)(struct _block_impl_4*);
} _block_desc_4_DATA = { 0, sizeof(struct _block_impl_4), _block_copy_4, _block_dispose_4};


static void _block_copy_4(struct _block_impl_4*dst, struct _block_impl_4*src) {
    _Block_object_assign((void*)&dst->weakSelf, (void*)src->weakSelf, 3/*BLOCK_FIELD_IS_OBJECT*/);
}


static void _block_dispose_4(struct _block_impl_4*src) {
    _Block_object_dispose((void*)src->weakSelf, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

struct _block_impl_4 {
  struct __block_impl impl;
  struct _block_desc_4* Desc;
  BlockTestViewController *const __weak weakSelf;
  _block_impl_4(void *fp, struct _block_desc_4 *desc, BlockTestViewController *const __weak _weakSelf, int flags=0) : weakSelf(_weakSelf) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};


static void _block_func_4(struct _block_impl_4 *__cself) {
  BlockTestViewController *const __weak weakSelf = __cself->weakSelf; // bound by copy

        ((void (*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)weakSelf, sel_registerName("person")), sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_99_49qsqpv90l58q7813rrhltjc0000gn_T_BlockTestViewController_8bb5c3_mi_4);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_99_49qsqpv90l58q7813rrhltjc0000gn_T_BlockTestViewController_8bb5c3_mi_5, ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)weakSelf, sel_registerName("person")), sel_registerName("name")));
}

复制代码

  blockTest方法中block_weak编译的相关cpp代码如下所示:

    __attribute__((objc_ownership(weak))) typeof(self) weakSelf = self;

    void(*block_weak)(void);
    
    struct _block_impl_4 block_impl_4{(void *)_block_func_4, &_block_desc_4_DATA, weakSelf, 570425344};
    
    block_weak = (void (*)(void))&block_impl_4;

    ((void (*)(__block_impl *))((__block_impl *)block_weak)->FuncPtr)((__block_impl *)block_weak);
复制代码

  通过分析可以发现,Block捕获使用__weak修饰的OC对象与捕获普通OC对象底层结构中唯一的区别就在于其block_impl中会使用__weak关键字修饰捕获到的成员变量,并且所定义的变量

1.6 Block捕获Block变量

  第六组block生成的相关cpp代码如下所示:

struct _block_impl_5 {
  struct __block_impl impl;
  struct _block_desc_5* Desc;
  struct __block_impl *block_weak;
  _block_impl_5(void *fp, struct _block_desc_5 *desc, void *_block_weak, int flags=0) : block_weak((struct __block_impl *)_block_weak) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};


static void _block_func_5(struct _block_impl_5 *__cself) {

    void (*block_weak__strong)() = (void (*__strong)())__cself->block_weak; // bound by copy

    ((void (*)(__block_impl *))((__block_impl *)block_weak)->FuncPtr)((__block_impl *)block_weak);
}


static void _block_copy_5(struct _block_impl_5*dst, struct _block_impl_5*src) {
    _Block_object_assign((void*)&dst->block_weak, (void*)src->block_weak, 7/*BLOCK_FIELD_IS_BLOCK*/);
}


static void _block_dispose_5(struct _block_impl_5*src) {
    _Block_object_dispose((void*)src->block_weak, 7/*BLOCK_FIELD_IS_BLOCK*/);
}


static struct _block_desc_5 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct _block_impl_5*, struct _block_impl_5*);
  void (*dispose)(struct _block_impl_5*);
} _block_desc_5_DATA = { 0, sizeof(struct _block_impl_5), _block_copy_5, _block_dispose_5};
复制代码

  blockTest方法中block_block编译的相关cpp代码如下所示:

    void(*block_block)(void);
    
    struct _block_impl_5 block_impl_5{(void *)_block_func_5, &_block_desc_5_DATA, block_weak, 570425344};
    
    block_block = (void (*)(void))&block_impl_5;

    ((void (*)(__block_impl *))((__block_impl *)block_block)->FuncPtr)((__block_impl *)block_block);
复制代码

  通过分析可以发现,Block捕获Block对象,只是将捕获的Block对象赋值给其成员变量block_weak

1.7 Block捕获变量总结

  通过对以上Block源码进行静态分析可以发现,当每定义一个Block时,在编译器编译的过程中都会针对这个Block生成一个对应的结构体(不管之前是否编译过与当前Block类型相同的Block),如果被捕获变量(不管是block、基本数据类型还是OC对象)被__block关键字修饰,那么就会另外编译生成一个名为__Block_Byref_捕获变量名的结构体,并将这个被__block关键字修饰的局部变量的值(Block对象以及OC对象是指针值)拷贝到所对应的成员变量中,然后 __Block_impl_block名类型结构体中就会定义一个此结构体类型指针的成员变量,如果被捕获变量是被__weak修饰的,会使用__attribute__((objc_ownership(weak)))修饰这个局部变量,并且这个局部变量不管是被拷贝到__Block_impl_block名类型结构体或者__Block_Byref_捕获变量名的结构体中所对应的成员变量都会被__weak修饰,如果没有任何关键字修饰这个局部变量,那么在__Block_impl_block名类型结构体中就会有定义一个与此局部变量类型相同的局部变量。

2. Block类型

  经过对Block底层结构的静态分析,对Block已经有了一定的熟悉以及了解,但是在运行时,又是怎么样的情况呢?我们还不得而知,因此我们再来动态的分析Block代码。

  首先,在BlockTestViewController.m文件中添加如下blockTypeTest方法,并在viewDidLoad方法中进行调用。

//定义的全局变量
NSString *globalStr = @"全局变量";

- (void)blockTypeTest {
    //1. 捕获局部变量的Block
    int b = 20;
    void(^block_b)(void) = ^{

        NSLog(@"b - %d",b);
    };
    
    block_b();
    
    //2. 捕获全局变量的BlocK
    
    void(^block_c)(void) = ^{

        NSLog(@"globalStr - %@", globalStr);
    };
    
    block_c();
    //3. __weak关键字修饰block
    int a = 18;
    void(__weak ^block_a)(void) = ^{

        NSLog(@"a - %d",a);
    };
    
    block_a();
}

复制代码

  在每组Block初始化处打上断点,编译运行程序

2.1 堆Block

  程序执行到block_b的初始化处,显示汇编代码,如下图所示:

image.png

  在初始化创建Block对象时又调用了objc_retainBlock函数。

  添加一个objc_retainBlock函数的符号断点,来到这个断点之后,会发现在objc_retainBlock函数中又调用了_Block_copy函数,如下图所示:

image.png

  添加一个_Block_copy函数的符号断点,来到符号断点,看到如下汇编代码:

image.png

  可以发现的是_Block_copy是属于libsystem_blocks库中的函数,但是libsystem_blocks库并不是开源的,但是我们先来看看_Block_copy函数做了什么事情,先查看寄存器x0数据,如下所示:

image.png

  然后在_Block_copy函数返回之前打上断点,然后看看返回值,如下图所示:

image.png

  可以发现的是block_a这个Block对象由原来的__NSStackBlock__类型变为了__NSMallocBlock类型,而且地址也是不一样的,其signature值就是此其Block签名,将此类型包装成NSMethodSignature对象,打印其信息,如下图所示:

image.png

2.2 全局Block

  程序执行到block_c的初始化处,显示汇编代码,如下图所示:

image.png

  同样调用了objc_retainBlock函数,过掉断点,让程序执行到_Block_copy函数,查看传入的参数,如下图所示:

image.png

  然后在_Block_copy函数返回之前打上断点,然后看看返回值,如下图所示:

image.png

  可发现在_Block_copy函数调用前后,block_c都是__NSGlobalBlock__类型Block,并且地址也未发生改变,因此__NSGlobalBlock__类型Block与全局变量一样,是在程序运行时就被初始化完毕了。

2.3 栈Block

  程序执行到block_a的定义处,显示汇编代码,如下图所示:

image.png

  发现调用了objc_initWeakobjc_loadWeakRetainedobjc_release以及objc_destroyWeak函数,并没有像前两个block一样调用objc_retainBlock函数,因此使用__weak修饰的Block对象,仍旧是一个__NSStackBlock__类型Block

3.Block底层源码探究

  到目前为止,我们对Block底层数据结构已经很熟悉,但是如果你想知道_Block_copy函数做了什么样的操作,还需要探究libsystem_blocks库,但由于这个库并不开源,因此我们只能另辟蹊径,来查看另一个开源库libclosure中的代码了,你可以点击下载,密码为z0wf

3.1 _Block_copy函数及相关结构体探究

  下载完毕之后,搜索_Block_copy关键字,找到如下代码:

image.png

  根据对以上代码的分析,我们再来看看Block_layout结构体,如下图所示:

image.png

  这个结构体中定义了一个struct Block_descriptor_1 *类型的结构体指针成员变量descriptor,其代码如下图所示:

image.png

  可以发现的是,这里定义了Block_descriptor_1Block_descriptor_2以及Block_descriptor_3三种结构体,跟我们所看到的源码的底层结构十分类似,但是为什么这里我们看不到Block_layout中定义的Block_descriptor_2以及Block_descriptor_3结构体指针变量呢?那该如何获取得到Block_descriptor_2中的copy以及dispose函数指针进行调用呢?带着这样的疑惑在源码中搜索Block_descriptor_3这个关键字,找到如下图代码:

image.png

  分析以上代码,我们可以得到Block_layout结构体变量的内存结构图,大致如下图所示:

image.png

  除了以上几种结构体类型之外,我们在源码中看到的结构体类型还有Block_byref,其定义如下图所示:

image.png

3.1 copy函数探究

  分析完以上代码以及结构体类型之后,我们再来重点分析Block_copy中调用的_Block_call_copy_helper函数,其代码如下图所示:

image.png

  在这个函数中调用了内联函数_Block_get_copy_function,其代码如下图所示:

image.png

  其中_Block_get_copy_fn函数代码如下所示:

image.png

image.png

  经过以上分析我们可以知道_Block_call_copy_helper的作用就是获取Block中的copy函数并进行调用,传入的参数分别为result(指向堆的Block_layout类型指针)以及aBlock(指向栈的Block_layout类型指针),而在之前我们所探索的源码中copy函数中又调用了_Block_object_assign函数,其源码如下所示:

image.png

  在这个函数中,会根据捕获的变量类型分别进行处理,如果捕获的类型是一个OC对象,则对增加其引用计数值,如果捕获的变量类型是Block对象,则会调用Block_copy对这个Block对象进行操作,如果捕获的变量类型是使用__weak__block修饰的OC对象或者Block对象的包装类型Byref对象,就会调用_Block_byref_copy函数对这个Byref对象进行相应操作,_Block_byref_copy函数代码如下图所示:

image.png

  在这个函数中,又对src2指针的成员变量byref_keep函数指针进行了调用,而在之前的源码(1.4 Block捕获使用__block修饰的OC对象)中,这个byref_keep被赋值为如下所示的函数:

image.png

  也就是取出__Block_byref_person2_1结构体变量person2中成员变量person2,如下图所示:

image.png

  然后再调用_Block_object_assign函数对person2这个OC对象的引用计数进行加1操作。

未完待续....

文章分类
iOS
文章标签