深入探索Block(一)

目录

一 ,Block的本质

 二,Block的变量捕获

三,Block 的类型

一 ,Block的本质

.m 文件代码如下

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int age = 10;
        void (^block)(void) = ^{
            NSLog(@"this is  a block-%d",age);
        };
        age = 20;
        block();
    }
    return 0;
}

 使用终端命令 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m 查看底层实现的文件。

 结论如下:

  • Block 内部也有一个isa指针
  • 封装了函数调用以及函数调用环境内的OC对象 

 二,Block的变量捕获

  • 局部变量:auto/static 修饰的局部变量,Block 都会将其捕获到block内部;auto修饰的局部变量,block 通过值传递的方式访问;static修饰的局部变量,block 通过指针传递的方式访问;
  • 全局变量:block 不将其捕获,直接访问。
  • 以下举例只是验证了Block 捕获的是局部变量,至于static的捕获,希望大家自己动手实现一下,去验证为何 static 声明的局部变量在block 调用之前修改其值,block 内部引用的其值也跟着变。
#import "XZPerson.h"

@implementation XZPerson
//block 会将 _name 捕获到block内部吗?
- (void)test{
    void (^block)(void) = ^{
        NSLog(@"-------------%p",self->_name);
    };
    block();
}
@end
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
/*
1.使用终端命令 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc XZPerson.m 生成c++文件
2.block 本质代码如下,可以看到,block 是通过捕获ZPerson *self,进而访问成员变量_name.
 
struct __XZPerson__test_block_impl_0 {
  struct __block_impl impl;
  struct __XZPerson__test_block_desc_0* Desc;
  XZPerson *self;
  __XZPerson__test_block_impl_0(void *fp, struct __XZPerson__test_block_desc_0 *desc, XZPerson *_self, int flags=0) : self(_self) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

3.通过cpp 文件可以看出,test的底层实现如下:
static void _I_XZPerson_test(XZPerson * self, SEL _cmd) {
    void (*block)(void) = ((void (*)())&__XZPerson__test_block_impl_0((void *)__XZPerson__test_block_func_0, &__XZPerson__test_block_desc_0_DATA, self, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

每个方法都有两个隐藏的参数 self(方法调用者),SEL _cmd(方法的实现),两个都是局部变量,block会捕获局部变量到其内部也可从此得出结论
*/

三,Block 的类型

上面提到block 是oc对象,既然是对象就能调用oc的class 方法,查看其元类对象以及父类对象

 void (^block)(void) = ^{
            NSLog(@"this is  a block");
        };
        NSLog(@"block-1%@",[block class]);  /*结果:__NSGlobalBlock__*/
        NSLog(@"block-2%@",[[block class] superclass]); /*结果:__NSGlobalBlock*/
        NSLog(@"block-3%@",[[[block class] superclass] superclass]);  /*结果:NSBlock*/
        NSLog(@"block-4%@",[[[[block class] superclass] superclass] superclass]); /*结果:NSObject*/

通过以下方法可知道block的类型

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        void (^block1)(void) = ^{
            NSLog(@"this is  one  block");
        };
        int age =10;
        void (^block2)(void) = ^{
            NSLog(@"this is  two  block-%d",age);
        };
        NSLog(@"%@-%@-%@",[block1 class],[block2 class],[^{
            NSLog(@"this is  three  block-%d",age);
        } class]);
        //__NSGlobalBlock__ - __NSMallocBlock__ - __NSStackBlock__
    }
    return 0;
}

这里大家也许有个疑问?使用终端命令 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m 查看底层c++文件这三个block生成的block 类型都为同一种,_NSConcreteStackBlock?这个不用奇怪,我们一切以运行时为主。clang 只是LLVM编译器的一部分,也许LLVm在运行过程中做了一定的处理。导致运行的结果是三种不同类型的Block。

总结:block3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型

  1. __NSGlobalBlock__ _NSConcreteGlobalBlock
  2. __NSStackBlock__ _NSConcreteStackBlock
  3. __NSMallocBlock__ _NSConcreteMallocBlock
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //MRC下探索:
        //没有访问auto变量的block 就是global类型的block;
        //访问全局+静态的局部变量都是global类型的block
        //访问auto的就是stack 类型的block,注意ARC下打印的是malloc类型,如果想要探索本质,记得吧ARC关闭哦。
        void (^block1)(void) = ^{
            NSLog(@"this is  one  block");
        } ;
         //__NSGlobalBlock__ 调用copy 还是__NSGlobalBlock__
        int age =10;
        void (^block2)(void) = ^{
            NSLog(@"this is  two  block-%d",age);
        } ;
        //MRC:__NSStackBlock__调用copy 变成了__NSMallocBlock__即从栈区变到了堆区
        NSLog(@"%@-%@",[block1 class],[block2 class]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_33726122/article/details/82837745