美文网首页
Block原理分析

Block原理分析

作者: 会跑的鱼_09 | 来源:发表于2020-11-25 15:30 被阅读0次

    block类型

    我们都知道block有三种类型:NSGlobalBlock、NSStackBlock、NSMallocBlock,一个block没有引用外部变量的那它就是NSGlobalBlock,如果引用了外部变量,它是先声明成NSStackBlock然后经过block_copy方法变成了NSMallocBlock。在我们开发过程中如果直接打印某个block对象,是无法看到其类型的。但通过lldb调试后在汇编层是可以查看具体类型变化的(注意要用真机)。有如下代码:

    __block NSString *bb = [NSString stringWithFormat:@"mm"];
    void(^blockbb)(void) = ^{
         NSLog(@"%@",bb);
    };
    blockbb();
    

    断点后查看汇编代码:


    step1.png

    然后来到


    step2.png

    按住Ctrol+Step Into,此时读取寄存器

    lldb) register read x0
          x0 = 0x000000016f07f6f8
    (lldb) po 0x000000016f07f6f8
    <__NSStackBlock__: 0x16f07f6f8>
     signature: "v8@?0"
     invoke   : 0x100d86320 (/private/var/containers/Bundle/Application/2400E0E1-2CBA-42B3-B540-F104929D5B03/004-Block结构与签名.app/004-Block结构与签名`__main_block_invoke.10)
     copy     : 0x100d862b0 (/private/var/containers/Bundle/Application/2400E0E1-2CBA-42B3-B540-F104929D5B03/004-Block结构与签名.app/004-Block结构与签名`__copy_helper_block_e8_32r)
     dispose  : 0x100d862c0 (/private/var/containers/Bundle/Application/2400E0E1-2CBA-42B3-B540-F104929D5B03/004-Block结构与签名.app/004-Block结构与签名`__destroy_helper_block_e8_32r)
    

    继续往下执行,直到来到_Block_copy的汇编代码


    step3.png

    在最下面的ret处打个断点,再次读取寄存器x0

    (lldb) register read x0
          x0 = 0x00000002820d0000
    (lldb) po 0x00000002820d0000
    <__NSMallocBlock__: 0x2820d0000>
     signature: "v8@?0"
     invoke   : 0x100d86320 (/private/var/containers/Bundle/Application/2400E0E1-2CBA-42B3-B540-F104929D5B03/004-Block结构与签名.app/004-Block结构与签名`__main_block_invoke.10)
     copy     : 0x100d862b0 (/private/var/containers/Bundle/Application/2400E0E1-2CBA-42B3-B540-F104929D5B03/004-Block结构与签名.app/004-Block结构与签名`__copy_helper_block_e8_32r)
     dispose  : 0x100d862c0 (/private/var/containers/Bundle/Application/2400E0E1-2CBA-42B3-B540-F104929D5B03/004-Block结构与签名.app/004-Block结构与签名`__destroy_helper_block_e8_32r)
    

    clang分析

    在main.m中有如下代码:

    void(^block1)(void) = ^{
        printf("global block");
    };
    block1();
    
    int a = 10;
    void(^block2)(void) = ^{
        printf("stack block %d",a);
    };
    block2();
    
    __block int b = 10;
    void(^block3)(void) = ^{
        printf("stack block %d",b++);
    };
    block3();
    
    NSString *aa = @"aa";
    void(^blockaa)(void) = ^{
        NSLog(@"%@",aa);
    };
    blockaa();
    
    __block NSString *bb = [NSString stringWithFormat:@"mm"];
    void(^blockbb)(void) = ^{
        bb = [bb stringByAppendingString:@"+++"];
        NSLog(@"%@",bb);
    };
    blockbb();
    

    我们利用clang编译器生成.cpp文件后,查看一下block的底层实现,命令:

    clang -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.2.sdk main.m
    

    先看一下block1的生成代码:

     void(*block1)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    ((void (*)(__block_impl *))((__block_impl *)block1)->FuncPtr)((__block_impl *)block1);
    
    //block1对应的结构体
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    //block1对应的方法实现
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
        printf("global block");
    }
    //block1对应的block描述
    static struct __main_block_desc_0 {
      size_t reserved;
      size_t Block_size;
    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
    

    block2的生成代码,注意int a未使用__block:

    int a = 10;
    //此处是把a的值直接传进去了
    void(*block2)(void) = ((void (*)())&__main_block_impl_1((void *)__main_block_func_1, &__main_block_desc_1_DATA, a));
    ((void (*)(__block_impl *))((__block_impl *)block2)->FuncPtr)((__block_impl *)block2);
    
    //block2的结构体只比block1多一个int a,其他不变
    struct __main_block_impl_1 {
      struct __block_impl impl;
      struct __main_block_desc_1* Desc;
      int a;
      __main_block_impl_1(void *fp, struct __main_block_desc_1 *desc, int _a, int flags=0) : a(_a) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __main_block_func_1(struct __main_block_impl_1 *__cself) {
        //基础类型,是值传递
        int a = __cself->a; // bound by copy
        printf("stack block %d",a);
    }
    
    static struct __main_block_desc_1 {
      size_t reserved;
      size_t Block_size;
    } __main_block_desc_1_DATA = { 0, sizeof(struct __main_block_impl_1)};
    

    看一下block3的,int b使用了__block,所以整个变量b被包装成了__Block_byref类型的对象,拿到的b的指针地址,方便后续对原始的b进行操作:

    __attribute__((__blocks__(byref))) __Block_byref_b_0 b = {(void*)0,(__Block_byref_b_0 *)&b, 0, sizeof(__Block_byref_b_0), 10};
    void(*block3)(void) = ((void (*)())&__main_block_impl_2((void *)__main_block_func_2, &__main_block_desc_2_DATA, (__Block_byref_b_0 *)&b, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)block3)->FuncPtr)((__block_impl *)block3);
    
    //__block声明的int b会被包装成block_byref类型的对象
    struct __Block_byref_b_0 {
      void *__isa;
    __Block_byref_b_0 *__forwarding;//直接指向外面定义的int b包装对象
     int __flags;
     int __size;
     int b;//block内部他用的b
    };
    
    struct __main_block_impl_2 {
      struct __block_impl impl;
      struct __main_block_desc_2* Desc;
      __Block_byref_b_0 *b; // by ref
      __main_block_impl_2(void *fp, struct __main_block_desc_2 *desc, __Block_byref_b_0 *_b, int flags=0) : b(_b->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __main_block_func_2(struct __main_block_impl_2 *__cself) {
      __Block_byref_b_0 *b = __cself->b; // bound by ref
      //所以此处操作__forwarding->b,就是操作的外部的包装对象
      printf("stack block %d",(b->__forwarding->b)++);
    }
    static void __main_block_copy_2(struct __main_block_impl_2*dst, struct __main_block_impl_2*src) {_Block_object_assign((void*)&dst->b, (void*)src->b, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static void __main_block_dispose_2(struct __main_block_impl_2*src) {_Block_object_dispose((void*)src->b, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static struct __main_block_desc_2 {
      size_t reserved;
      size_t Block_size;
      void (*copy)(struct __main_block_impl_2*, struct __main_block_impl_2*);
      void (*dispose)(struct __main_block_impl_2*);
    } __main_block_desc_2_DATA = { 0, sizeof(struct __main_block_impl_2), __main_block_copy_2, __main_block_dispose_2};
    

    blockaa的,NSString *aa和上面的int a没啥区别,都没对变量进行再次包装,在block中打印aa时也是直接拿的外部传进来的aa的指针进行的:

    NSString *aa = (NSString *)&__NSConstantStringImpl__var_folders_np_137s52wn33b_l8qp8rpx951m0000gq_T_main_e269d6_mi_0;
    void(*blockaa)(void) = ((void (*)())&__main_block_impl_3((void *)__main_block_func_3, &__main_block_desc_3_DATA, aa, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)blockaa)->FuncPtr)((__block_impl *)blockaa);
    
    struct __main_block_impl_3 {
      struct __block_impl impl;
      struct __main_block_desc_3* Desc;
      NSString *aa;
      __main_block_impl_3(void *fp, struct __main_block_desc_3 *desc, NSString *_aa, int flags=0) : aa(_aa) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __main_block_func_3(struct __main_block_impl_3 *__cself) {
      NSString *aa = __cself->aa; // bound by copy
    
      NSLog((NSString *)&__NSConstantStringImpl__var_folders_np_137s52wn33b_l8qp8rpx951m0000gq_T_main_e269d6_mi_1,aa);
            }
    static void __main_block_copy_3(struct __main_block_impl_3*dst, struct __main_block_impl_3*src) {_Block_object_assign((void*)&dst->aa, (void*)src->aa, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    static void __main_block_dispose_3(struct __main_block_impl_3*src) {_Block_object_dispose((void*)src->aa, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    static struct __main_block_desc_3 {
      size_t reserved;
      size_t Block_size;
      void (*copy)(struct __main_block_impl_3*, struct __main_block_impl_3*);
      void (*dispose)(struct __main_block_impl_3*);
    } __main_block_desc_3_DATA = { 0, sizeof(struct __main_block_impl_3), __main_block_copy_3, __main_block_dispose_3};
    

    最复杂的blockbb的,这里对NSString *bb变量的包装,比上面的int b还多了一层__Block_byref_id_object_copy和__Block_byref_id_object_dispose,这是因为对象类型不仅需要传值,需要考虑引用计数增加以及释放:

    __attribute__((__blocks__(byref))) __Block_byref_bb_1 bb = {(void*)0,(__Block_byref_bb_1 *)&bb, 33554432, sizeof(__Block_byref_bb_1), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSString * _Nonnull (*)(id, SEL, NSString * _Nonnull, ...))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl__var_folders_np_137s52wn33b_l8qp8rpx951m0000gq_T_main_e269d6_mi_2)};
    void(*blockbb)(void) = ((void (*)())&__main_block_impl_4((void *)__main_block_func_4, &__main_block_desc_4_DATA, (__Block_byref_bb_1 *)&bb, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)blockbb)->FuncPtr)((__block_impl *)blockbb);
    
    struct __Block_byref_bb_1 {
      void *__isa;
    __Block_byref_bb_1 *__forwarding;
     int __flags;
     int __size;
     void (*__Block_byref_id_object_copy)(void*, void*);
     void (*__Block_byref_id_object_dispose)(void*);
     NSString *bb;
    };
    
    static void __Block_byref_id_object_copy_131(void *dst, void *src) {
     _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
    }
    static void __Block_byref_id_object_dispose_131(void *src) {
     _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
    }
    
    struct __main_block_impl_4 {
      struct __block_impl impl;
      struct __main_block_desc_4* Desc;
      __Block_byref_bb_1 *bb; // by ref
      __main_block_impl_4(void *fp, struct __main_block_desc_4 *desc, __Block_byref_bb_1 *_bb, int flags=0) : bb(_bb->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    static void __main_block_func_4(struct __main_block_impl_4 *__cself) {
      __Block_byref_bb_1 *bb = __cself->bb; // bound by ref
    
        (bb->__forwarding->bb) = ((NSString *(*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)(bb->__forwarding->bb), sel_registerName("stringByAppendingString:"), (NSString *)&__NSConstantStringImpl__var_folders_np_137s52wn33b_l8qp8rpx951m0000gq_T_main_6649bc_mi_3);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_np_137s52wn33b_l8qp8rpx951m0000gq_T_main_6649bc_mi_4,(bb->__forwarding->bb));
    }
    
    static void __main_block_copy_4(struct __main_block_impl_4*dst, struct __main_block_impl_4*src) {_Block_object_assign((void*)&dst->bb, (void*)src->bb, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static void __main_block_dispose_4(struct __main_block_impl_4*src) {_Block_object_dispose((void*)src->bb, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static struct __main_block_desc_4 {
      size_t reserved;
      size_t Block_size;
      void (*copy)(struct __main_block_impl_4*, struct __main_block_impl_4*);
      void (*dispose)(struct __main_block_impl_4*);
    } __main_block_desc_4_DATA = { 0, sizeof(struct __main_block_impl_4), __main_block_copy_4, __main_block_dispose_4};
    

    源码解读

    上面我们从编译器层面看一下block的实现原理,但block其实还有它的底层实现,例如_Block_copy、_Block_object_assign、_Block_object_dispose的具体实现是怎样的呢。可以通过查看block的底层libclosure-73源码来分析一下

    _Block_copy源码

    block对象在底层实现的关键方法

    void *_Block_copy(const void *arg) {
        struct Block_layout *aBlock;
    
        if (!arg) return NULL;
        
        // The following would be better done as a switch statement
        aBlock = (struct Block_layout *)arg;
        if (aBlock->flags & BLOCK_NEEDS_FREE) {
            // latches on high
            latching_incr_int(&aBlock->flags);
            return aBlock;
        }
        else if (aBlock->flags & BLOCK_IS_GLOBAL) {
            return aBlock; // gloal类型的,啥都不需要做
        }
        else { // statck类型的
            // Its a stack block.  Make a copy.
            struct Block_layout *result =
                (struct Block_layout *)malloc(aBlock->descriptor->size);
            if (!result) return NULL;
            memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
    #if __has_feature(ptrauth_calls)
            // Resign the invoke pointer as it uses address authentication.
            result->invoke = aBlock->invoke;
    #endif
            // reset refcount
            result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING);    // XXX not needed
            result->flags |= BLOCK_NEEDS_FREE | 2;  // logical refcount 1
            //对于需要持有外部变量的都会触发block对象的__main_block_copy方法
            _Block_call_copy_helper(result, aBlock);
            // Set isa last so memory analysis tools see a fully-initialized object.
            //转换成mallocBlock
            result->isa = _NSConcreteMallocBlock;
            return result;
        }
    }
    
    _Block_object_assign源码

    block对象捕获外部y变量的关键方法。对于需要持有外部变量的block,在转换成mallocBlock后都会调用其对应的__main_block_copy方法,而__main_block_copy方法就调用_Block_object_assign方法来对外部的包装对象进行赋值逻辑判断

    void _Block_object_assign(void *destArg, const void *object, const int flags) {
        const void **dest = (const void **)destArg;
       
        switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
            //未用__block修饰的外部变量,交给系统进行内存管理,如果是ARC的话会进行自动的retain和release,这也是为什么block会暂时持有外部对象的原因
            case BLOCK_FIELD_IS_OBJECT:        
            _Block_retain_object(object);
            *dest = object;
            break;
            
          //block类型的变量,再次走到上面的block_copy中来
          case BLOCK_FIELD_IS_BLOCK:
            *dest = _Block_copy(object);
            break;
        
          //__block修饰的变量,会进入到_Block_byref_copy
          case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
          case BLOCK_FIELD_IS_BYREF:
            *dest = _Block_byref_copy(object);
            break;
            
          //以下几种类型都不会持有对象了
          case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
          case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:        
            *dest = object;
            break;
    
          case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
          case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK  | BLOCK_FIELD_IS_WEAK:        
            *dest = object;
            break;
    
          default:
            break;
        }
    }
    
    _Block_byref_copy源码

    外部变量包装对象的复制方法

    static struct Block_byref *_Block_byref_copy(const void *arg) {
        
        // Block_byref  结构体
        struct Block_byref *src = (struct Block_byref *)arg;
    
        if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
            // src points to stack
            struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
            copy->isa = NULL;
            // byref value 4 is logical refcount of 2: one for caller, one for stack
            copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
            
            // 对传进来的对象再次copy,并且修改了src的forwarding为新的对象
            copy->forwarding = copy; // patch heap copy to point to itself
            src->forwarding = copy;  // patch stack to point to heap copy
            
            copy->size = src->size;
    
            if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
                // Trust copy helper to copy everything of interest
                // If more than one field shows up in a byref block this is wrong XXX
                struct Block_byref_2 *src2 = (struct Block_byref_2 *)(src+1);
                struct Block_byref_2 *copy2 = (struct Block_byref_2 *)(copy+1);
                copy2->byref_keep = src2->byref_keep;
                copy2->byref_destroy = src2->byref_destroy;
    
                if (src->flags & BLOCK_BYREF_LAYOUT_EXTENDED) {
                    struct Block_byref_3 *src3 = (struct Block_byref_3 *)(src2+1);
                    struct Block_byref_3 *copy3 = (struct Block_byref_3*)(copy2+1);
                    copy3->layout = src3->layout;
                }
                //此处是触发外部Block_byref_id_object_copy方法调用的关键,如果__block修饰的对象类型的变量,还需要进一步执行_Block_object_assign
                (*src2->byref_keep)(copy, src);
            }
            else {
                // Bitwise copy.
                // This copy includes Block_byref_3, if any.
                memmove(copy+1, src+1, src->size - sizeof(*src));
            }
        }
        // already copied to heap
        else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
            latching_incr_int(&src->forwarding->flags);
        }
        
        return src->forwarding;
    }
    

    相关文章

      网友评论

          本文标题:Block原理分析

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