美文网首页
iOS Block 深入讲解,变量截获以及__Block 修饰符

iOS Block 深入讲解,变量截获以及__Block 修饰符

作者: 孙掌门 | 来源:发表于2019-12-15 21:58 被阅读0次

Block 深入讲解,变量截获以及__Block 修饰符原理

block:block 是将函数及其上下文封装起来的对象

测试代码

- (void)testBlock{
    int i = 2;
    int (^block)(int) = ^ int (int num){
        return num * i;
    };
    block(2);
}

然后通过系统命令将他转化为C++代码,clang -rewrite-objc block.m,然后会生成一个 cpp 文件,打开cpp文件,找打如下格式的方法_I_block_testBlock , -I 表示实例方法,后面是类名,后面是方法名

static void _I_block_testBlock(block * self, SEL _cmd) {
    int i = 2;
    int (*block)(int) = ((int (*)(int))&__block__testBlock_block_impl_0((void *)__block__testBlock_block_func_0, &__block__testBlock_block_desc_0_DATA, i));
    ((int (*)(__block_impl *, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 2);
}

__block__testBlock_block_func_0 是一个函数指针,第二个 __block__testBlock_block_desc_0_DATA 是方法描述,最后一个是我们的局部变量也传进来了 i , block 定义的变量其实是个函数指针,

struct __block__testBlock_block_impl_0 {
  struct __block_impl impl;
  struct __block__testBlock_block_desc_0* Desc;
  int i;
  __block__testBlock_block_impl_0(void *fp, struct __block__testBlock_block_desc_0 *desc, int _i, int flags=0) : i(_i) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

__block_impl :

struct __block_impl {
  void *isa;//isa 指针
  int Flags;
  int Reserved;
  void *FuncPtr;// 函数指针
};

其中第一个为 isa 指针,证明block是对象的标志

总结 :

block 其实就是对象,封装了函数和上下稳定,block调用就是函数的调用

截获变量

int i = 2;
    int (^block)(int) = ^ int (int num){
        return num * i;
    };
    i = 10;
    block(2);

输出值是多少?

局部变量类型,有基本数据类型和对象类型,静态局部变量,全局变量,静态全局变量

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

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

3.以指针形式截获局部静态变量

4.不截获全局变量和静态全局变量

例子:

//
//  block.m
//  blogTest
//
//  Created by 孙承秀 on 2019/12/15.
//  Copyright © 2019 孙承秀. All rights reserved.
//

#import "block.h"
// 全局变量
int global_int = 5;
// 静态全局变量
static int static_global_int = 6;
@implementation block
- (void)testBlock{
    // 基本数据类型
    int i = 2;
    // 对象类型的局部变量
    __unsafe_unretained id unsafe_obj = nil;
    __strong id strong_obj = nil;
    
    // 局部静态变量
    static int static_int = 3;
    void (^block)(void) = ^ {
        NSLog(@"局部变量:%d",i);
        NSLog(@"局部变量(unsafe_unretained):%@",unsafe_obj);
        NSLog(@"局部变量(strong):%@",strong_obj);
        NSLog(@"局部静态变量:%d",static_int);
        NSLog(@"全局变量:%d",global_int);
        NSLog(@"全局静态变量:%d",static_global_int);
    };
    block();
    
}
@end

使用命令将其转化为c++代码 clang -rewrite-objc -fobjc-arc block.m

struct __block__testBlock_block_impl_0 {
  struct __block_impl impl;
  struct __block__testBlock_block_desc_0* Desc;
    // 截获局部变量的值
  int i;
    //连同所有权修饰符一起截获
  __unsafe_unretained id unsafe_obj;
  __strong id strong_obj;
    
    // 以指针形式截获局部变量
  int *static_int;
    // 没有全局变量和静态全局变量,说明不截获
    
    
  __block__testBlock_block_impl_0(void *fp, struct __block__testBlock_block_desc_0 *desc, int _i, __unsafe_unretained id _unsafe_obj, __strong id _strong_obj, int *_static_int, int flags=0) : i(_i), unsafe_obj(_unsafe_obj), strong_obj(_strong_obj), static_int(_static_int) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

通过查看源码,可以看到 block 的结构体里面,i 被直接截获值,我们定义的连个对象,连同修饰符都被截获进去,而局部静态变量是以指针的形式截获.

练习题


int i = 2;
    int (^block)(int) = ^ int (int num){
        return num * i;
    };
    i = 10;
    block(2);

 static int i = 2;
    int (^block)(int) = ^ int (int num){
        return num * i;
    };
    i = 10;
    block(2);

所以这两个题的返回结果就不用我说了吧,没有加 static 就是普通的局部变量,block 内部只截获他的值,之后修改就不在改变,而加了static修饰符之后,是以指针的形式截获,那么之后修改值也会生效。

__Block 修饰符

一般情况下,对被截获的变量进行赋值操作需要添加 __Block 修饰符

NSMutableArray *arr = [NSMutableArray array];
    void (^block)(void)=^{
        [arr addObject:@"123"];
    };
    block();

 NSMutableArray *arr1 = nil;
    
    void (^block1)(void)=^{
        
        arr1 = [NSMutableArray array];
        [arr1 addObject:@"456"];
    };
    block1();
    

上面两个题打印结果又是如何呢?答案是第二个,需要加上 __Block , 因为赋值需要加上,__Block

那什么时候不需要加上 __Block 修饰符呢?读了前面就一定知道了

1.静态局部变量

2.全局变量

3.静态全局变量

下面的例子的值:

__block int blokck_value = 2;
    int (^block)(int) = ^ int (int num){
        return num * blokck_value;
    };
    blokck_value = 10;
    block(2);

答案是20,被__Block修饰的变量最后变成了对象, blokck_value = 10,其实最后就变成了blokck_value ._forwarding->multiplier = 10,通过forwarding指针改变他的值

相关文章

网友评论

      本文标题:iOS Block 深入讲解,变量截获以及__Block 修饰符

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