Block

2018-08-31  本文已影响15人  曹来东

Block本质

int age = 10;//局部变量
void (^block)(void) = ^{
  NSLog(@"age is %d",age);//打印结果为10
}
age = 20;
block();

Block变量捕获

变量类型 是否捕获到Block内部 访问方式
局部变量 auto YES 值传递
局部变量 static YES 指针传递
全局变量 NO 直接访问

局部变量:在函数内部定义的变量
全局变量:在函数外部定义的变量
是字符串替换.
auto变量:离开作用域会自动销毁
static变量:全局变量 不会销毁.

//全局变量 不会捕获 直接访问
static number = 10;
//局部auto变量,值传递,局部变量会随时销毁,所以捕获到block内部.
int age = 10;//等价代码auto int age = 10;
//局部static变量;地址传递.局部变量会随时销毁,所以捕获到block内部.
static height = 10;
void (^block)(void) = ^{
  NSLog(@"age is %d",age);//打印结果为10,值传递.捕获
 NSLog(@"height is %d", height);//打印结果为20.地址传递,捕获
 NSLog(@"number is %d", number);//打印结果为20,直接访问,不捕获
}
age = 20;
height = 20;
number = 20;
block();

局部变量:栈段
全局变量:数据段
对象:堆
类对象:数据段

实例:

//LDPerson类中的test方法的实现
- (void) test{
//会捕获self对象,因为self是函数参数,函数
//参数是局部变量,局部变量会捕获到block中
 //并且以值传递的方式捕获
void (^block)(void) = ^{
          NSLog(@"%@",self);
}
block();
}
//LDPerson有一个name属性
- (void) test{
void (^block)(void) = ^{
//会捕获self对象,因为self是函数参数,函数
//参数是局部变量,局部变量会捕获到block中
//并且以值传递的方式捕获.捕获self之后,通过self->name来访问name属性
          NSLog(@"%@",_name);
}
block();
}

self是局部变量不是全局变量.

Block类型:

      void (^block)(void) = ^{
            NSLog(@"111");
        };
        NSLog(@"%@",[block class]);
        NSLog(@"%@",[[block class] superclass]);
        NSLog(@"%@",[[[block class] superclass] superclass]);
        NSLog(@"%@",[[[[block class] superclass] superclass] superclass]);
//打印结果:
2018-08-30 16:09:50.907192+0800 全局变量[27597:5794698] __NSGlobalBlock__
2018-08-30 16:09:50.907403+0800 全局变量[27597:5794698] __NSGlobalBlock
2018-08-30 16:09:50.907422+0800 全局变量[27597:5794698] NSBlock
2018-08-30 16:09:50.907436+0800 全局变量[27597:5794698] NSObject

可以看到block存在继承结构.是一个OC对象.

内存分区

应用程序内存分配
代码段
数据段
image.png

Block分类:

block类型 环境
NSGlobalBlock 没有访问auto变量
NSStackBlock 访问了aotu变量
NSMallocBlock NSStackBlock 调用了copy

每一种类型的block调用copy后的结果如下所示

block类型 副本源的存储区 复制效果
NSGlobalBlock 数据段 nothing
NSStackBlock 从栈复制到堆
NSMallocBlock 引用计数增加

ARC模式下编译器对Block自动进行copy操作的情况

编译器会自动对block进行copy(堆blockMallocBlock)

block属性建议写法

对象类型的auto变量

当block内部访问了对象类型的auto变量时:

  1. 如果是栈block,不会对auto变量产生强引用.
  2. 如果是堆block,会对Strong引用的auto变量产生强引用.
    如果block被拷贝到堆上,会调用block内部的copy函数,copy函数内部会调用_Block_object_assign函数,_Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用
    3.如果block从堆上移除,会调用block内部的dispose函数,dispose函数内部会调用_Block_object_dispose函数, _Block_object_dispose函数会自动释放引用的auto变量(release)
函数 调用时机
copy函数 栈Block复制到堆时
dispose函数 堆上的Block被废弃时

代码示例:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    LDPerson * person = [[LDPerson alloc] init];
    NSLog(@"1111");
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"dispatch_after 三秒后 执行的 代码");
        NSLog(@"%@",person);
    });
    NSLog(@"touchesBegan 执行完成");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    LDPerson * person = [[LDPerson alloc] init];
  __weak  LDPerson  *weakPerson = person;
    NSLog(@"1111");
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"dispatch_after 三秒后 执行的 代码");
        NSLog(@"%@", weakPerson);
    });
    NSLog(@"touchesBegan 执行完成");
}

__block内存管理

__Block

__block的__forwarding指针

image.png

代码示例:

/**
 ARC下 copy/strong 修饰都可以,
 MRC下 copy
 */
@property (nonatomic,copy) void (^block) (void);
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //block引用.局部变量,会捕获到结构体变量里面.值传递.不会对a引用
    auto int a = 10;
     //block引用.局部变量.且是OC对象类型会捕获到结构体变量里面.地址传递
    //该obj对象为强指针修饰.block对obj强引用,[obj retain]
    NSObject * obj = [[NSObject alloc] init];
    //弱指针局部对象变量.会捕获到结构体变量里面.地址传递
    //block对obj弱引用
    __weak NSObject * weakObj = obj;
    //__block 修饰的局部变量,在block内部才能被修改.
    //__block int b 会将b包装成一个OC对象.
    //__block 修饰的变量,block都会对其产生强引用
    __block int b = 10;
    //强引用block,会进行copy操作,所以该block是堆block
    NSMutableArray * arr = [NSMutableArray array];
    __block NSMutableArray * array = [NSMutableArray array];

    self.block = ^{
        b = 30;
        NSLog(@"%d",a);
        NSLog(@"%@",obj);
        NSLog(@"%@",weakObj);
        //此时没有修改arr指针的值,所以不需要__block修饰
        [arr addObject:@""];
        //此时修改了block外部变量,需要用__block
        //Variable is not assignable (missing __block type specifier)
        array = nil;
    };
    self.block();
}

对象类型的auto变量、__block变量

block内部访问变量类型 修饰关键字
对像 BLOCK-FIELD_ISOBJECT
__block 变量 BLOCK_FIELD_IS-BYREF

被__block修饰的对象类型

image.png
此时person为局部变量,代码29行之后person释放.
image.png
此时person为局部变量,block对person强引用.
上一篇 下一篇

猜你喜欢

热点阅读