美文网首页
iOS block及相关内容理解

iOS block及相关内容理解

作者: 晨曦中的花豹 | 来源:发表于2021-08-02 14:43 被阅读0次

    什么是block

    block就是包含了函数和函数参数的一个结构体(也可以理解为对象).
    Block可以拆分为三大块内容,

    • block内容生成的函数(本质就是函数调用),
    • block结构体,
    • 还有__block结构体(使用__block修饰后生成的结构体)

    block的具体实现原理:

    1.根据代码动态生成结构体
    __Block_byref_age_0
    __main_block_impl_0
    2.block的内容动态生成函数
    __main_block_func_0
    3.main函数中__block生成__block对象 --- age
    3.将age对象,函数地址传递给block结构体构造函数,生成Block结构体 --- block;
    4.在执行block时候,调用block->funcPtr执行函数(这里传入block,验证了block包含了函数和函数调用所需参数)

    例子

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            __block int age = 10;
            MJBlock block = ^{
                age = 30;
            };
            block();
        }
        return 0;
    }
    

    转换成c++后:

    //__block修饰参数结构体
    struct __Block_byref_age_0 {
      void *__isa;
    __Block_byref_age_0 *__forwarding;
     int __flags;
     int __size;
     int age;
    };
    
    //Block结构体,这是一个c++结构体,内部这个函数是构造函数,调用后返回结构体(对照OC init方法)
    struct __main_block_impl_0 {
      struct __block_impl impl;                     //函数
      struct __main_block_desc_0* Desc;             //相关描述(以及Block结构体的copy及释放方法,暂时不做具体分析)
      __Block_byref_age_0 *age;                     //__block修饰的参数
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;                          //这里将函数地址赋值给ptr属性
        Desc = desc;
      }
    };
    
    //block包含内容实际是个函数
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
        __Block_byref_age_0 *age = __cself->age; // block存储的__block对象(结构体)
        (age->__forwarding->age) = 30;  //使用__block对象内部指针__forwarding指向真是的__block对象,然后取出变量,进行赋值
    /*
             (这里使用__forwarding的原理有点混乱,
             有一种说法是block copy的时候__block也会从栈上copy到堆中,这里__forwarding就会指向堆中的__block对象,
             但是同时这个堆中的__block对象会赋值给堆中的block,所以取出的age就应该是堆中的__block age,
             无需在使用 __forwarding指针指向自己,
             所以这里这样使用真是目的还需要进一步探索)
            */
    }
    
    //main函数本身
    int main(int argc, const char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
            __Block_byref_age_0 age = {
                0,
                &age,               //age结构体对象本身的地址
                0,
                sizeof(__Block_byref_age_0),
                10                  //外部变量
            };
            MJBlock block = &__main_block_impl_0(
                                                 __main_block_func_0,           //block内容封装的函数
                                                 &__main_block_desc_0_DATA,     //block本身的功能(包含block本身的copy和释放的逻辑)
                                                 &age,                          //__block结构体
                                                 570425344
                                                 );
            block->FuncPtr(block);  //执行block
        }
        return 0;
    }
    

    以上是关于block以及__block的实现原理的个人理解,要把它想象成为一个包裹,包含着函数imp和调用环境(参数等).

    细节

    1.ARC中block会自动从栈中copy到堆中,MRC中不会,需要手动copy;

    2.使用__block的原因,为什么要使用__block修饰呢?这里说ARC下:是为了让普通的局部变量(基本数据类型)可以在block内部,也就是函数中使用,看了上边的例子,大家就可以理解为什么要这样做了,如果不用__block将局部的变量包含起来,一旦出了作用域,age马上便释放了,所以ARC中使用__block是为了,让其可以在接下来的函数中继续使用;

    3.__block修饰的变量,在生成__block结构体时,如果在MRC下不会进行retain操作,可以理解为弱引用,可以用来解决MRC下的循环引用问题,但是在ARC下会对引用的对象进行retain操作,强引用,这里可以使用__weak进一步修饰,出现了如同
    __block __weak Person *weakPerson = person;
    的方式,但是对于对象,我们不需要__block修饰了,因为对象本身就在堆中,可以用指针的方式获取到,所以不用__block修饰,最后就是我们常见的
    __weak Person *weakPerson = person;或者__weak typeof(person) weakPerson = person;

    4.一般面试中会问你__block和__weak的区别,其实本身他们的作用就是不一样的,__block意义在于对局部变量的持久捕获(个人词汇...),让其在block调用的时候依然存在,而__weak是在ARC下,表示对对象的捕获为弱捕获(个人词汇...),不对其引用计数产生影响;

    相关文章

      网友评论

          本文标题:iOS block及相关内容理解

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