iOS探索:Block解析浅谈

栏目: IOS · 发布时间: 7年前

内容简介:这段代码执行完Block(2)返回的值是多少呢?-------答案是12我们在什么情况下使用__block修饰符呢?一般情况下,对被截获变量进行赋值操作需要添加__block修饰符,此时Block返回的是8,这里是为什么呢,我们只是用了__block来修饰
  • Block是将函数及其执行上下文封装起来的对象

接下来让我们通过源码来看一看Block的本质

iOS探索:Block解析浅谈
  • 我们在一个方法中写了三行代码,第一行是定义了一个局部变量,第二行是一个Block,第三行是这个Block的调用

这里我们通过一个clang的编译命令clang -rewrite-objc xxx.m来看一下源码的实现

iOS探索:Block解析浅谈
  • 我们的那段代码通过编译器编写后,首先第一行I代表的是一个实例方法后面的是对象和方法名,传了两个参数一个是self,一个是选择器因子

  • 然后我们方法中的第一行代码在编译后没有发生改变,我们着重看一下Block方法编译后的改变

  • 首先我们可以看到__BlockOneObj__testMethod_block_impl_0这样一个结构体,在这个结构体中传递了几个参数,第一个参数(void*)__BlockOneObj__testMethod_block_func_0我们通过名字可以知道这是一个无类型的函数指针,第二个参数&__BlockOneObj__testMethod_block_desc_0_DATA是一个Block相关描述的结构体然后取地址符,第三个参数muIntNum就是我们定义的局部变量。最后取这个结构体地址强制转换赋值给我们定义的这个Block

然后我们来看看__BlockOneObj__testMethod_block_impl_0这个结构体中有什么具体操作,如下图

iOS探索:Block解析浅谈

其中第一个结构体里面又是什么数据结构呢,请看下图

iOS探索:Block解析浅谈

在我们上面介绍的结构体下面还有一个函数,具体解释请看下图

iOS探索:Block解析浅谈

那么什么是Block的调用呢

iOS探索:Block解析浅谈

Block的调用其实就是函数的调用,从源码中我们可以看出来

  • 首先先对这个Block进行一个强制类型转换(__block_impl *)Block

  • 之后又取出它之中的成员变量FuncPtr(函数指针),找到我们上面解析的结构体和函数,在其中拿到对应的函数调用,然后把其中的参数传递进去,一个参数是我们这个Block本身,一个是我们传递的2,然后就回去调用__BlockOneObj__testMethod_block_func_0函数,最终进行调用

Block截获变量

首先我们先来看一段代码

- (void)testMethod {
    
    int muIntNum = 6;
    int(^Block)(int) = ^int(int num){
        return num *muIntNum;
    };
    
    muIntNum = 4;
    Block(2);
}

复制代码

这段代码执行完Block(2)返回的值是多少呢?-------答案是12 接下来我们看一下为什么是12以及Block截获变量的本质是什么

  • 对于基本数据类型的局部变量截获其值

  • 对于对象类型的局部变量连同其所有权修饰符一起截获

  • 对于局部静态变量是以指针形式去截获

  • 对于全局变量和静态全局变量不截获

下面直接上代码

#import "BlockTwoObj.h"

//全局变量
int global_var = 4;
//全局静态变量
static int static_global_var = 5;

@implementation BlockTwoObj

- (void)testMethodTwo {
    
    //基本数据类型的局部变量
    int var = 1;
    //对象类型的的局部变量
    __unsafe_unretained id unsafe_obj = nil;
    __strong id strong_obj = nil;
    
    //局部静态变量
    static int static_var = 3;
    
    void(^Block)(void) = ^{
        
        NSLog(@"基本数据类型局部变量:%d", var);
        NSLog(@"对象类型局部变量(__unsafe_unretained修饰):%@", unsafe_obj);
        NSLog(@"对象类型局部变量(__strong修饰):%@", strong_obj);
        
        NSLog(@"局部静态变量:%d", static_var);
        
        NSLog(@"全局变量:%d", global_var);
        NSLog(@"全局静态变量:%d", static_global_var);
    };
    
    Block();
}

@end
复制代码

接下来我们通过clang命令clang -rewrite-objc -fobjc-arc xxx.m来看一下源码

iOS探索:Block解析浅谈
  • 在这张图中可以很清晰的看到Block中的变量截获, 其中需要注意的是对于局部的静态变量截获的是指针,也就是说如果后面这个局部静态变量发生了修改,那么Block中使用的是最新的值

__block修饰符

我们在什么情况下使用__block修饰符呢?一般情况下,对被截获变量进行赋值操作需要添加__block修饰符, 这里需要注意的是赋值不等于是使用,切记!!!

例如在下面的代码中是否需要__block修饰符来修饰

NSMutableArray *muArr = [[NSMutableArray alloc] init];
    
    void(^Block)(void) = ^{
        //这里只是做了添加操作,并非赋值,所以不需要用__block进行修饰
        [muArr addObject:@"111"];
    };
    
    Block();
复制代码

那么在下面的代码段当中呢?

__block NSMutableArray *muArrOther = nil;
     void(^BlockOther)(void) = ^{
        //这里做了赋值操作,所以需要用__block进行修饰,否则会出现编译报错
        muArrOther = [NSMutableArray array];
    };
    
    BlockOther();
复制代码

对变量进行赋值时

  • 需要__block修饰符修饰的是局部变量(包括基本数据类型和对象类型)

  • 不需要__block修饰符修饰的是静态局部变量、全局变量和静态全局变量,因为对于全局变量和静态全局变量不涉及到变量的截获,而对于静态局部变量呢,是通过使用指针来操作对应的变量的,所以也不需要修饰

下面请看一段代码,还是我们上面的那个例子

- (void)testMethod {
    
    __block int muIntNum = 6;
    int(^Block)(int) = ^int(int num){
        return num *muIntNum;
    };
    
    muIntNum = 4;
    Block(2);
}
复制代码

此时Block返回的是8,这里是为什么呢,我们只是用了__block来修饰

  • 因为在这里会发生一个非常奇妙的变化, __block修饰的变量变成了对象

请看下面的流程图

iOS探索:Block解析浅谈
  • 首先__block int muIntNum会被转化成第一个这样一个结构体,其中具有isa指针,我们也可以理解成一个对象

  • 从这个角度来看muIntNum经过编译后就会变成一个对象,通过__forwarding指针去找到对应的对象,然后进行赋值

  • 刚才我们看到的代码段是在栈上,在__block变量中有一个__forwarding指针,而这个指针指向的是自己, 这里要注意的是前提是在栈上,如果在堆上,这个__forwarding指针指向的就不是自己了,在下面会讲到

  • 所以在栈上我们修改这个变量的值,就会通过__forwarding指针找到自己本省去修改这个变量的值

那么这里有一个问题就是我们在栈上这个__forwarding指向的是自己到底有什么用呢?我们完全可以通过访问成员变量来修改,为什么还需要这个指针呢,请继续往下看


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Algorithms on Strings, Trees and Sequences

Algorithms on Strings, Trees and Sequences

Dan Gusfield / Cambridge University Press / 1997-5-28 / USD 99.99

String algorithms are a traditional area of study in computer science. In recent years their importance has grown dramatically with the huge increase of electronically stored text and of molecular seq......一起来看看 《Algorithms on Strings, Trees and Sequences》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具