美文网首页Block 探索之路
Block (深入理解)原理探究

Block (深入理解)原理探究

作者: 三月木头 | 来源:发表于2019-12-28 08:11 被阅读0次

探索场景需求

  1. 局部变量
  2. 全局静态变量
  3. 局部对象(举例NSMutableArray)
  4. 栈对象(NSString *str = @"AAA"; 这种情况,情况个例)
    我们会根据这几种情况,根据打印指针,打印结果,还有探索源码进行分析。

先说结论

  1. 局部变量首先在heap区开辟空间, 被捕获后copy变量值,另外在stack区开辟一个新的空间存储,block执行后显示的是最开始的数据。
  2. 全局静态变量在static区开辟空间,被捕获不影响其地址,捕获后内部使用仍然是其static区的地址,所以显示变化后的数据。
  3. 局部对象NSMutabblArray,是在heap堆区开辟内存空间,被捕获后还是捕获其原地址指针,所以block执行会根据可变数组变化显示。
  4. 栈对象。同局部变量一样不展开。

设计相关代码

    //此处是设计的变量、局部对象
    int a = 10;
    static int b = 100;
    NSMutableArray *array1 = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
    
    NSLog(@"a: %p",&a);
    NSLog(@"b: %p",&b);
    NSLog(@"array1: %p",array1);
    self.myblock = ^(SecViewController * aa) {
        NSLog(@"a:%d",a);
        NSLog(@"a: %p",&a);
        NSLog(@"b: %d",b);
        NSLog(@"b: %p",&b);
        NSLog(@"array1:%p",array1);
        
    };
    a = 20;
    b = 200;
    [array1 addObject:@"3"];
    self.myblock(self);

查看一下打印结果:


2019-12-25 19:52:14.569872+0800 BlockTest[9499:776114] a: 0x7ffee83f7fac
2019-12-25 19:52:14.570121+0800 BlockTest[9499:776114] b: 0x107808828
2019-12-25 19:52:14.570277+0800 BlockTest[9499:776114] array1: 0x600003598720
2019-12-25 19:52:32.933938+0800 BlockTest[9499:776114] a:10
2019-12-25 19:52:33.675075+0800 BlockTest[9499:776114] a: 0x600003598b38
2019-12-25 19:52:34.803982+0800 BlockTest[9499:776114] b: 200
2019-12-25 19:52:35.967736+0800 BlockTest[9499:776114] b: 0x107808828
2019-12-25 19:52:47.685754+0800 BlockTest[9499:776114] array1:0x600003598720

从打印的数据可以验证结论:
我们局部变量,在block内被捕获,但是通过观察地址,我们发现这个被捕获的局部变量,这是block之前的那个变量进行局部copy而已,地址的指针已经改变。而局部静态变量b一直存在静态区,所以打印的是它变化后的值。对于局部对象数组array1而言,生成时候的地址就是在stack区,block捕获后的地址还是原地址,所以对数组进行操作后,打印出来的是操作后的数据。

探索不同场景下 block 内部实现

首先设置研究环境,我们都知道Object-C中,是通过Clang/LLVM对OC代码进行编译,那我们编译一下block所在的.m文件,查看一下其编译成了什么。

  1. 首先我们cd 进入其所在的文件夹。
  2. 通过Clang语音进行编译,然后在对应文件查看相关的C++文件。
xcrun -sdk iphonesimulator clang -rewrite-objc SecViewController.m

1. 源码解析

static void _I_SecViewController_viewDidLoad(SecViewController * self, SEL _cmd) {
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("SecViewController"))}, sel_registerName("viewDidLoad"));
    int a = 10;
    static int b = 100;
    NSMutableArray *array1 = ((NSMutableArray * _Nonnull (*)(id, SEL, ObjectType _Nonnull, ...))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("arrayWithObjects:"), (id _Nonnull)(NSString *)&__NSConstantStringImpl__var_folders_qw_scdxdg7x5zxfqh31vt91xl9h0000gn_T_SecViewController_83b1d8_mi_0, (NSString *)&__NSConstantStringImpl__var_folders_qw_scdxdg7x5zxfqh31vt91xl9h0000gn_T_SecViewController_83b1d8_mi_1, __null);

    NSLog((NSString *)&__NSConstantStringImpl__var_folders_qw_scdxdg7x5zxfqh31vt91xl9h0000gn_T_SecViewController_83b1d8_mi_2,&a);
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_qw_scdxdg7x5zxfqh31vt91xl9h0000gn_T_SecViewController_83b1d8_mi_3,&b);
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_qw_scdxdg7x5zxfqh31vt91xl9h0000gn_T_SecViewController_83b1d8_mi_4,array1);
    ((void (*)(id, SEL, myBlock))(void *)objc_msgSend)((id)self, sel_registerName("setMyblock:"), ((void (*)(SecViewController *))&__SecViewController__viewDidLoad_block_impl_0((void *)__SecViewController__viewDidLoad_block_func_0, &__SecViewController__viewDidLoad_block_desc_0_DATA, a, &b, array1, 570425344)));
    a = 20;
    b = 200;
    ((void (*)(id, SEL, ObjectType _Nonnull))(void *)objc_msgSend)((id)array1, sel_registerName("addObject:"), (id _Nonnull)(NSString *)&__NSConstantStringImpl__var_folders_qw_scdxdg7x5zxfqh31vt91xl9h0000gn_T_SecViewController_83b1d8_mi_10);
    ((myBlock (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("myblock"))(self);
}

看起来乱糟糕,让我们只提取一下block的信息出来也就是

 self.myblock = ^(SecViewController * aa) {
        NSLog(@"a:%d",a);
        NSLog(@"a: %p",&a);
        NSLog(@"b: %d",b);
        NSLog(@"b: %p",&b);
        NSLog(@"array1:%p",array1);
        
    };

对应的源码如下:

    ((void (*)(id, SEL, myBlock))(void *)objc_msgSend)((id)self, sel_registerName("setMyblock:"), ((void (*)(SecViewController *))&__SecViewController__viewDidLoad_block_impl_0((void *)__SecViewController__viewDidLoad_block_func_0, &__SecViewController__viewDidLoad_block_desc_0_DATA, a, &b, array1, 570425344)));

再简化一下,去掉相关的类型编译

   ( (id, SEL, myBlock) objc_msgSend)(self, sel_registerName("setMyblock:"), (&__SecViewController__viewDidLoad_block_impl_0(__SecViewController__viewDidLoad_block_func_0, &__SecViewController__viewDidLoad_block_desc_0_DATA, a, &b, array1, 570425344)));

我们看到上面代码。形参id SEL myBlock,我们再抽离一下,只看看myBlock 对应的代码

&__SecViewController__viewDidLoad_block_impl_0(__SecViewController__viewDidLoad_block_func_0, &__SecViewController__viewDidLoad_block_desc_0_DATA, a, &b, array1, 570425344))

我们看到这里的时候需要进去再看看__SecViewController__viewDidLoad_block_impl_0这个到底是什么东东? 根据了解信息猜测,这应该就是myblock在heap区下对应的block信息,看看源码验证一下

struct __SecViewController__viewDidLoad_block_impl_0 {
  struct __block_impl impl;
  struct __SecViewController__viewDidLoad_block_desc_0* Desc;
  int a;
  int *b;
  NSMutableArray *array1;
  __SecViewController__viewDidLoad_block_impl_0(void *fp, struct __SecViewController__viewDidLoad_block_desc_0 *desc, int _a, int *_b, NSMutableArray *_array1, int flags=0) : a(_a), b(_b), array1(_array1) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

果然,如咱们所料,这个就是代码中myBlock下对应的信息。在这里我们可以初步看到,结构体内部拿到的参数中,a是变量,b、 array1是指针。
此处 fp的实参是什么呢?根据上面myBlock内部代码我们很容易发现是__SecViewController__viewDidLoad_block_func_0,源码如下:

static void __SecViewController__viewDidLoad_block_func_0(struct __SecViewController__viewDidLoad_block_impl_0 *__cself, SecViewController *aa) {
  int a = __cself->a; // bound by copy
  int *b = __cself->b; // bound by copy
  NSMutableArray *array1 = __cself->array1; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_qw_scdxdg7x5zxfqh31vt91xl9h0000gn_T_SecViewController_83b1d8_mi_5,a);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_qw_scdxdg7x5zxfqh31vt91xl9h0000gn_T_SecViewController_83b1d8_mi_6,&a);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_qw_scdxdg7x5zxfqh31vt91xl9h0000gn_T_SecViewController_83b1d8_mi_7,(*b));
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_qw_scdxdg7x5zxfqh31vt91xl9h0000gn_T_SecViewController_83b1d8_mi_8,&(*b));
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_qw_scdxdg7x5zxfqh31vt91xl9h0000gn_T_SecViewController_83b1d8_mi_9,array1);

    }

在这里我们就发现了,此处拿到的a只是一个值传递,b、array1是指针也就是地址传递,所以我们在内部打印时候,只能打印捕获到的a数据,但是通过指针可以获取到改变后的数据b、array1.

综上:
我们block内只能操纵局部变量原始数据,可以捕获静态变量、堆对象的最新改变后的数据。


Git项目代码:下载Block


相关文章

网友评论

    本文标题:Block (深入理解)原理探究

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