美文网首页
关于block(二)----为什么使用copy,为什么使用__b

关于block(二)----为什么使用copy,为什么使用__b

作者: stonly916 | 来源:发表于2018-05-17 17:00 被阅读0次

    不知道大家使用block的时候有没有想过:

    为什么block赋值需要用copy?

    Blk blockTest0 = ^() {
        printf("assa\n");
    };
    Blk blockTest = [blockTest0 copy];
    

    对于全局block来说,就和全局常量一样,copy就是引用,且没有引用计数的增减,在app结束前不会释放,对于这类block就相当于一个无参数的c函数,我们只需要一个函数指针来调用它,blockTest就是它的函数指针。

        __block int h = 9;
        printf("h在block外的地址%p\n",&h);
        //__NSStackBlock__,直接在栈中被调用
        void(^staBlock1)(void) = ^{
            printf("h在block内的地址%p\n",&h);
            h = 11;
            printf("h在block内赋值后的地址%p\n",&h);
        }();
    

    对于上面这个block,我们先不考虑__block的存在,我们引用了外部变量h,h是在block外是局部变量,存储在栈中,栈内存由系统控制释放,当我们在调用staBlock1的时候,可能变量h已经在栈中被释放了,这导致后续调用h引发程序崩溃;
    当block有赋值操作时,这个block就可能在离开这个作用域后被调用,为了避免这种情况,OC采用了copy方法,

    struct __ClangProxy__staff_block_impl_8 {
      struct __block_impl impl;
      struct __ClangProxy__staff_block_desc_8* Desc;
      . . .;//引用的外部变量
      __ClangProxy__staff_block_impl_8(void *fp, struct __ClangProxy__staff_block_desc_8 *desc, int *_tt, int flags=0) : tt(_tt) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    

    由于对外部变量的引用是存放在block的结构体中的,只要将这个结构体重新copy一份,放到堆上,引用堆上的变量(堆上变量根据引用计数来管理释放与否),也就不会有“调用已被释放的变量”这种情况。

    为什么在block内有变值操作的变量,需要使用__block修饰?

    copy问题解决了“调用已被释放的变量”的问题,但是另一个问题接踵而来,

    在上面代码中,block使用copy方法后,变量h被copy到了堆中,那么它就与原来的h互不相干了,此时在block对h赋值h=11,就是堆上的h变为11,而block外部的h是存储在栈中的,它还是9,这与编程意图有悖,这个编程意图很显然是要同时改变block内外h的值,保持block内外变量的一致性才是编程的要求。
    这个问题,OC使用了__block来解决了,使用这个 关键字修饰 会将h从一个基本类型int变量,变换为一个结构体的成员变量,例如:

    struct __Block_byref_h_0 {
      void *__isa;
    __Block_byref_h_0 *__forwarding;
     int __flags;
     int __size;
     int h;
    };
    

    这个结构体我在关于block的clang源码中有讲到。使用__block后,block结构体中引用的就不是基本类型h了,而是结构体__Block_byref_h_0 *struct_h;,在block外,当还没有发生block的copy操作时,__Block_byref_h_0 *struct_h存储在栈中,结构体中还有一个成员__Block_byref_h_0 *__forwarding是指向自身的指针,再看使用h的时候,我们的调用方法是struct_h->__forwarding->h,这并不是多此一举;在block发生copy操作后,__Block_byref_h_0 *struct_h结构体会被copy到堆中__Block_byref_h_0 *malloc_struct_h,这时__forwarding就发挥它的作用了,它指向了堆中的结构体__forwarding = malloc_struct_h,我们调用h的时候相当于是struct_h->malloc_struct_h->h,这里也就解决了变量copy到堆后,block赋值导致变量在block内外值不一样的问题了。

    相关文章

      网友评论

          本文标题:关于block(二)----为什么使用copy,为什么使用__b

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