• Block中__block实现原理


    三.Block中__block实现原理

    我们继续研究一下__block实现原理。

    1.普通非对象的变量

    先来看看普通变量的情况。

    #import <Foundation/Foundation.h>
    
    int main(int argc, const char * argv[]) {
    
        __block int i = 0;
    
        void (^myBlock)(void) = ^{
            i ++;
            NSLog(@"%d",i);
        };
    
        myBlock();
    
        return 0;
    }

    把上述代码用clang转换成源码。

    struct __Block_byref_i_0 {
      void *__isa;
    __Block_byref_i_0 *__forwarding;
     int __flags;
     int __size;
     int i;
    };
    
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      __Block_byref_i_0 *i; // by ref
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      __Block_byref_i_0 *i = __cself->i; // bound by ref
    
            (i->__forwarding->i) ++;
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_3b0837_mi_0,(i->__forwarding->i));
        }
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->i, (void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static struct __main_block_desc_0 {
      size_t reserved;
      size_t Block_size;
      void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
      void (*dispose)(struct __main_block_impl_0*);
    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
    int main(int argc, const char * argv[]) {
        __attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 0};
    
        void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344));
    
        ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
    
        return 0;
    }

    从源码我们能发现,带有 __block的变量也被转化成了一个结构体__Block_byref_i_0,这个结构体有5个成员变量。第一个是isa指针,第二个是指向自身类型的__forwarding指针,第三个是一个标记flag,第四个是它的大小,第五个是变量值,名字和变量名同名。

    __attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 0};

    源码中是这样初始化的。__forwarding指针初始化传递的是自己的地址。然而这里__forwarding指针真的永远指向自己么?我们来做一个实验。

    //以下代码在MRC中运行
        __block int i = 0;
        NSLog(@"%p",&i);
    
        void (^myBlock)(void) = [^{
            i ++;
            NSLog(@"这是Block 里面%p",&i);
        }copy];

    我们把Block拷贝到了堆上,这个时候打印出来的2个i变量的地址就不同了。

    0x7fff5fbff818
    <__NSMallocBlock__: 0x100203cc0>
    这是Block 里面 0x1002038a8

    地址不同就可以很明显的说明__forwarding指针并没有指向之前的自己了。那__forwarding指针现在指向到哪里了呢?

    Block里面的__block的地址和Block的地址就相差1052。我们可以很大胆的猜想,__block现在也在堆上了。

    出现这个不同的原因在于这里把Block拷贝到了堆上。

    由第二章里面详细分析的,堆上的Block会持有对象。我们把Block通过copy到了堆上,堆上也会重新复制一份Block,并且该Block也会继续持有该__block。当Block释放的时候,__block没有被任何对象引用,也会被释放销毁。

    __forwarding指针这里的作用就是针对堆的Block,把原来__forwarding指针指向自己,换成指向_NSConcreteMallocBlock上复制之后的__block自己。然后堆上的变量的__forwarding再指向自己。这样不管__block怎么复制到堆上,还是在栈上,都可以通过(i->__forwarding->i)来访问到变量值。


     


    所以在__main_block_func_0函数里面就是写的(i->__forwarding->i)。



    作者:一缕殇流化隐半边冰霜
    链接:http://www.jianshu.com/p/ee9756f3d5f6
    來源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    各种素材类精品网站
    Java如何设置代理ip
    Spring事务的实现方式和原理以及隔离级别?
    Spring 是什么?
    线程池中阻塞队列的作用?为什么是先添加队列而不是先创建最大线程?
    为什么用线程池?解释下线程池参数
    并发三大特性
    ThreadLocal的原理和使用场景。
    谈谈对线程安全的理解
    sleep,wait,join,yield
  • 原文地址:https://www.cnblogs.com/feng9exe/p/7486099.html
Copyright © 2020-2023  润新知