iOS block及相关内容理解

2021-08-02  本文已影响0人  晨曦中的花豹

什么是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下,表示对对象的捕获为弱捕获(个人词汇...),不对其引用计数产生影响;

上一篇 下一篇

猜你喜欢

热点阅读