美文网首页
Block的三种类型

Block的三种类型

作者: 孙小猴猴猴 | 来源:发表于2023-09-21 17:32 被阅读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操作,在堆中创建一个新副本)

    相关文章

      网友评论

          本文标题:Block的三种类型

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