OC

block的本质

Posted on 2021-03-24,3 min read

目录:block的本质、变量捕获、类型、copy、__block修饰符

本质

  • block本质上也是一个OC对象,它内部也有个isa指针
  • block是封装了函数调用(函数地址)以及函数调用环境(参数、外部局部变量等)的OC对象
  • 图示:
    block的底层实现.png

变量捕获

  • 局部变量

    类型 捕获到block内部 访问问方式
    auto 值传递
    static 指针传递
    auto int age = 10; // auto: 自动变量,离开作用域就销毁
    static int height = 100;
    void (^block)(void) = ^{
        NSLog(@"age is %d, height is %d",age ,height); // 捕获变量(capture)
    };
    
    age = 20;
    height = 200;
    block(); // age is 10, height is 200
    
  • 全局变量

    类型 捕获到block内部 访问问方式
    全局变量 直接访问
  • self

    self也会被捕获到block内部,注意,self实际上是局部变量!!!指针传递

    oc当中,函数方法实际上有2个隐藏参数,例(MyPerson *self,SEL _cmd),其中,self是作为局部变量传递进来的

block的类型

block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型。

  1. NSGlobalBlock ( _NSConcreteGlobalBlock ) 没有访问auto变量的
  2. NSStackBlock ( _NSConcreteStackBlock ) 访问了auto变量(需在非ARC)
  3. NSMallocBlock ( _NSConcreteMallocBlock ) NSStackBlock 调用了copy(需在非ARC)
    block类型

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

block类型 副本源的配置存储域 复制效果
NSGlobalBlock 程序的数据区域 什么也不做
NSStackBlock 从栈复制到堆
NSMallocBlock 引用计数增加

block的copy

在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如:

  • blcok作为函数返回值时

        MJBlock block() {
            int age = 10;
            return ^{
                NSLog(@"age = %d",age);
            };
        }
    
  • 将block赋值给 __strong 指针时

        int age = 10;
        void (^blcok)(void) = ^{
            NSLog(@"age = %d",age);
        };
        block();
    
  • block作为Cocoa API中方法名含有usingBlock的方法参数时

  • block作为GCD API的方法参数时

__block修饰符

  • 作用:

    1. __block可以用于解决block内部无法修改auto变量值的问题
    2. __block不能修饰全局变量、静态变量
    3. 编译器会将__block变量包装成一个对象
  • 内存管理:

    1. 当block在栈上时,并不会对 __block 产生强引用
    2. 当block被copy到堆时,会调用block内部的copy函数,其内部会对 __block 变量形成强引用
    3. 当block从堆中移除时,会调用block内部的dispose函数,其内部会自动释放引用的 __block 变量(release)

下一篇: Category分类→

loading...