iOS之Block相关问题

2021-03-18  本文已影响0人  枫叶无处漂泊

一、Block是什么?

二、Block有几种类型

一、NSGlobalBlock:就是全局数据区的Block对象

那么什么时候block的类型是_NSConcreteGlobalBlock类型?

二、NSMallocBlock

在ARC情况下,编译器会根据情况自动将栈上的block复制到堆上,那么几种情况的Block的类型进行copy?

大多数情况都是block作为函数返回值时进行copy操作。怎么判定是block类型是NSMallocBlock?

三、栈block(NSStackBlock)

栈上的block随时会被销毁,受系统控制。怎么声明NSStackBlock类型?

三、对着三种类型block进行copy操作后的结果?

四、对应的block相关面试题

一、局部变量截获 是值截获

NSInteger num = 3;

NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){
        
    return n*num;
};
num = 1;
NSLog(@"%zd",block(2));

这里的输出是6而不是2,原因就是对局部变量num的截获是值截获。

同样,在block里如果修改变量num,也是无效的,甚至编译器会报错。

NSMutableArray * arr = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
    
void(^block)(void) = ^{
        
    NSLog(@"%@",arr);//局部变量
    [arr addObject:@"4"];
};
    
[arr addObject:@"3"];
 arr = nil;
 block();
 

打印为1,2,3

局部对象变量也是一样,截获的是值,而不是指针,在外部将其置为nil,对block没有影响,而该对象调用方法会影响。

二、局部静态变量截获 是指针截获。

 static  NSInteger num = 3;
 NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){
        
    return n*num;
};
num = 1;
NSLog(@"%zd",block(2));

打印为:2 而不是6
因为是指针截获,在block里去修改变量m,也是有效的。

三、全局变量,静态全局变量截获:不截获,直接取值。

我们看一下各种数据类型clang编译结果:

static NSInteger num3 = 300;
NSInteger num4 = 3000;
- (void)blockTest {
    
    NSInteger num = 30;
   static NSInteger num2 = 3;
    __block NSInteger num5 = 30000;
    void(^block)(void) = ^{
        
        NSLog(@"%zd",num);//局部变量
        
        NSLog(@"%zd",num2);//静态变量
        
        NSLog(@"%zd",num3);//全局变量
        
        NSLog(@"%zd",num4);//全局静态变量
        
        NSLog(@"%zd",num5);//__block修饰变量
    };
    
    block();
}

编译成CPP文件后:

struct __WYTest__blockTest_block_impl_0 {
  struct __block_impl impl;
  struct __WYTest__blockTest_block_desc_0* Desc;
  NSInteger num;//局部变量
  NSInteger *num2;//静态变量
  __Block_byref_num5_0 *num5; // by ref//__block修饰变量
  __WYTest__blockTest_block_impl_0(
  void *fp, 
  struct __WYTest__blockTest_block_desc_0 *desc, 
  NSInteger _num, 
  NSInteger *_num2, 
  __Block_byref_num5_0 *_num5, int flags=0) : num(_num), num2(_num2), num5(_num5->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

其中WYTest是文件名,blockTest是方法名,这些可以忽略。

_block_impl_0结构体中:

struct __Block_byref_num5_0 {
    void *__isa;
    __Block_byref_num5_0 *__forwarding;
    int __flags;
    int __size;
    NSInteger num5;
};

从这个结构体中表明:

为什么要通过__forwarding指针完成对count变量的读写修改?

下面通过代码来验证一下:

__block int num5 = 10;
NSLog(@"block捕获前地址:%p",&block_var);
        
void (^myblock)(void) =  ^{
          
    NSLog(@"num:%d", num5);
};
        
NSLog(@"block捕获后地址:%p",&num5);
num5 = 20;
myblock();

打印的结果表明捕获前后的地址是变化的并且值改变了

block捕获前地址:0x7ffeefbff4e8
block捕获后地址:0x102968608
num5:20

通过以上可以表明:

上一篇 下一篇

猜你喜欢

热点阅读