Block的三种类型

2023-09-21  本文已影响0人  孙小猴猴猴

在没有访问局部变量的情况下,Block的本质类似于一个闭包,它具有参数和返回值,并可以像普通函数一样存储在全局区,被称为"全局Block"。
如果我们在一个函数内部创建了一个block,该block内部代码引用了该函数内的某些局部变量(此时该block为栈block(stackBlock)),为了防止在block调用时引用的局部变量在原始作用域被销毁,我们需要把block以及该局部变量copy到堆上(此时该block为堆block(mallocBlock)),以确保block可以被正常执行。

auto变量 & static变量

// 在声明变量时,如果没有添加static关键字,那么默认为auto变量,在函数调用结束后自动销毁
(auto) int a = 10;
// 静态变量,储存于全局区,方法调用结束后不会销毁
static int a = 10;

1.全局block

如果block中没有访问任何外部变量,则将被存储在全局区中,不会被销毁(这种不访问外部变量的情况下我们一般会直接定义方法)

- (void)viewDidLoad {
    [super viewDidLoad];
    void (^block1)(void) = ^{};     
    NSLog(@"%@", [block1 class]); // __NSGlobalBlock__
}

2.MRC模式中的栈block

如果block中引用了auto变量, 那么block的将被存储在栈中。在MRC模式下方法调用结束后block的内存会被回收。

- (void)viewDidLoad {
    [super viewDidLoad];
    int age = 10;
    void (^block1)(void) = ^(){
        NSLog(@"%d", age);
    };
    NSLog(@"%@", [block1 class]); // __NSStackBlock__
}
2.1 MRC模式中栈block导致的问题

由于stackBlock引用的block在getBlock方法执行完成后就被销毁了,所以当在viewDidLoad方法中调用stackBlock时,age的值发生了错误

#import "ViewController.h"

typedef void (^Myblock)(void);
Myblock stackBlock;

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self getBlock];
    // __NSStackBlock__
    NSLog(@"%@", [stackBlock class]);
    // -307953584
    stackBlock();
}

- (void)getBlock {
    int age = 100;
    stackBlock = ^(){
        NSLog(@"%d", age);
    };
}

@end
2.1 解决MRC模式中栈block导致的问题

getBlock方法中将栈block赋值给stackBlock时,我们对栈block进行一次copy操作,这样就会将该栈block的副本存储在堆中,该副本不会随着方法执行结束而销毁。

- (void)viewDidLoad {
    [super viewDidLoad];
    [self getBlock];
    // __NSMallocBlock__
    NSLog(@"%@", [stackBlock class]);
    // -100
    stackBlock();
}

- (void)getBlock {
    int age = 100;
    stackBlock = [^(){
        NSLog(@"%d", age);
    } copy];
}

3 堆block

在MRC模式下,堆Block是由栈Block拷贝而来,需要程序员手动调用copy方法来实现。
而在ARC模式下,为了安全起见,xcode会对栈block的赋值行为自动进行一次copy操作(即使赋值给以Strong修饰的成员变量,依然会进行一次copy操作,在堆中创建一个新副本)

上一篇下一篇

猜你喜欢

热点阅读