美文网首页面试
iOS中Block捕获变量

iOS中Block捕获变量

作者: HoooChan | 来源:发表于2020-01-17 11:32 被阅读0次

    Block捕获变量:

    • 局部变量:基本数据类型:捕获值;对象型:捕获值和所有权修饰符

    • 静态局部变量:捕获地址

    • 全局变量:不捕获

    • 静态全局变量:不捕获

    捕获局部变量:非对象、对象、__block(非对象)、__block(对象)。

    捕获非对象:

    void blockStudy()
    {
        dispatch_block_t myBlock;
        {
            int a = 0;
            myBlock = ^() {
                if (a > 0) {
                }
            };
        }
    }
    
    // ==>
    
    void blockStudy()
    {
        dispatch_block_t myBlock;
        {
            int a = 0;
            myBlock = ((void (*)())&__blockStudy_block_impl_0((void *)__blockStudy_block_func_0, &__blockStudy_block_desc_0_DATA, a));
        }
    }
    
    struct __blockStudy_block_impl_0 {
      struct __block_impl impl;
      struct __blockStudy_block_desc_0* Desc;
      int a;
      __blockStudy_block_impl_0(void *fp, struct __blockStudy_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    static struct __blockStudy_block_desc_0 {
      size_t reserved;
      size_t Block_size;
    } __blockStudy_block_desc_0_DATA = { 0, sizeof(struct __blockStudy_block_impl_0)};
    

    捕获对象:

    void blockStudy()
    {
        dispatch_block_t myBlock;
        {
            MyObject *myObject = [MyObject new];
            myBlock = ^() {
                myObject = [MyObject new];
            };
        }
    }
    
    // ==>
    
    void blockStudy()
    {
        dispatch_block_t myBlock;
        {
            MyObject *myObject = ((MyObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("MyObject"), sel_registerName("new"));
            myBlock = ((void (*)())&__blockStudy_block_impl_0((void *)__blockStudy_block_func_0, &__blockStudy_block_desc_0_DATA, myObject, 570425344));
        }
    }
    
    struct __blockStudy_block_impl_0 {
      struct __block_impl impl;
      struct __blockStudy_block_desc_0* Desc;
      MyObject *__strong myObject;
      __blockStudy_block_impl_0(void *fp, struct __blockStudy_block_desc_0 *desc, MyObject *__strong _myObject, int flags=0) : myObject(_myObject) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    static struct __blockStudy_block_desc_0 {
      size_t reserved;
      size_t Block_size;
      void (*copy)(struct __blockStudy_block_impl_0*, struct __blockStudy_block_impl_0*);
      void (*dispose)(struct __blockStudy_block_impl_0*);
    } __blockStudy_block_desc_0_DATA = { 0, sizeof(struct __blockStudy_block_impl_0), __blockStudy_block_copy_0, __blockStudy_block_dispose_0};
    
    static void __blockStudy_block_copy_0(struct __blockStudy_block_impl_0*dst, struct __blockStudy_block_impl_0*src) {_Block_object_assign((void*)&dst->myObject, (void*)src->myObject, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    

    可以发现__blockStudy_block_desc_0相比捕获非对象的多了两个函数指针:copydispose。当block被拷贝到堆上面时copy函数会执行,调用_Block_object_assign拷贝捕获的对象的地址并增加该对象的引用计数。

    捕获__block(非对象)

    void blockStudy()
    {
        dispatch_block_t myBlock;
        {
            __block int myInt = 0;
            myBlock = ^() {
                myInt = 1;
            };
        }
    }
    
    // ==>
    
    void blockStudy()
    {
        dispatch_block_t myBlock;
        {
            __attribute__((__blocks__(byref))) __Block_byref_myInt_0 myInt = {(void*)0,(__Block_byref_myInt_0 *)&myInt, 0, sizeof(__Block_byref_myInt_0), 0};
            myBlock = ((void (*)())&__blockStudy_block_impl_0((void *)__blockStudy_block_func_0, &__blockStudy_block_desc_0_DATA, (__Block_byref_myInt_0 *)&myInt, 570425344));
        }
    }
    
    struct __Block_byref_myInt_0 {
      void *__isa;
    __Block_byref_myInt_0 *__forwarding;
     int __flags;
     int __size;
     int myInt;
    };
    
    struct __blockStudy_block_impl_0 {
      struct __block_impl impl;
      struct __blockStudy_block_desc_0* Desc;
      __Block_byref_myInt_0 *myInt; // by ref
      __blockStudy_block_impl_0(void *fp, struct __blockStudy_block_desc_0 *desc, __Block_byref_myInt_0 *_myInt, int flags=0) : myInt(_myInt->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    static struct __blockStudy_block_desc_0 {
      size_t reserved;
      size_t Block_size;
      void (*copy)(struct __blockStudy_block_impl_0*, struct __blockStudy_block_impl_0*);
      void (*dispose)(struct __blockStudy_block_impl_0*);
    } __blockStudy_block_desc_0_DATA = { 0, sizeof(struct __blockStudy_block_impl_0), __blockStudy_block_copy_0, __blockStudy_block_dispose_0};
    
    static void __blockStudy_block_copy_0(struct __blockStudy_block_impl_0*dst, struct __blockStudy_block_impl_0*src) {_Block_object_assign((void*)&dst->myInt, (void*)src->myInt, 8/*BLOCK_FIELD_IS_BYREF*/);}
    

    这里和捕获对象自由变量的block相比同样也有copy函数指针,不过在调用_Block_object_assign传的第三个参数不一样。

    捕获__block(对象)

    void blockStudy()
    {
        dispatch_block_t myBlock;
        {
            __block MyObject *myObject = [MyObject new];
            myBlock = ^() {
                myObject = [MyObject new];
            };
        }
    }
    
    // ==>
    
    void blockStudy()
    {
        dispatch_block_t myBlock;
        {
            __attribute__((__blocks__(byref))) __Block_byref_myObject_0 myObject = {(void*)0,(__Block_byref_myObject_0 *)&myObject, 33554432, sizeof(__Block_byref_myObject_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((MyObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("MyObject"), sel_registerName("new"))};
            myBlock = ((void (*)())&__blockStudy_block_impl_0((void *)__blockStudy_block_func_0, &__blockStudy_block_desc_0_DATA, (__Block_byref_myObject_0 *)&myObject, 570425344));
        }
    }
    
    struct __Block_byref_myObject_0 {
      void *__isa;
    __Block_byref_myObject_0 *__forwarding;
     int __flags;
     int __size;
     void (*__Block_byref_id_object_copy)(void*, void*);
     void (*__Block_byref_id_object_dispose)(void*);
     MyObject *__strong myObject;
    };
    
    struct __blockStudy_block_impl_0 {
      struct __block_impl impl;
      struct __blockStudy_block_desc_0* Desc;
      __Block_byref_myObject_0 *myObject; // by ref
      __blockStudy_block_impl_0(void *fp, struct __blockStudy_block_desc_0 *desc, __Block_byref_myObject_0 *_myObject, int flags=0) : myObject(_myObject->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    static struct __blockStudy_block_desc_0 {
      size_t reserved;
      size_t Block_size;
      void (*copy)(struct __blockStudy_block_impl_0*, struct __blockStudy_block_impl_0*);
      void (*dispose)(struct __blockStudy_block_impl_0*);
    } __blockStudy_block_desc_0_DATA = { 0, sizeof(struct __blockStudy_block_impl_0), __blockStudy_block_copy_0, __blockStudy_block_dispose_0};
    
    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 __blockStudy_block_copy_0(struct __blockStudy_block_impl_0*dst, struct __blockStudy_block_impl_0*src) {_Block_object_assign((void*)&dst->myObject, (void*)src->myObject, 8/*BLOCK_FIELD_IS_BYREF*/);}
    

    这里的block同样有copy函数指针,但是__block变量的结构体__Block_byref_myObject_0也多了两个函数指针:__Block_byref_id_object_copy__Block_byref_id_object_dispose。当block被拷贝到堆上面时,__block变量的结构体__Block_byref_myObject_0也会被拷贝到堆上面,这时__Block_byref_id_object_copy就会被调用。

    _Block_object_assign

    看看_Block_object_assign的源码:

    /*
     * When Blocks or Block_byrefs hold objects then their copy routine helpers use this entry point
     * to do the assignment.
     */
    void _Block_object_assign(void *destAddr, const void *object, const int flags) {
        //printf("_Block_object_assign(*%p, %p, %x)\n", destAddr, object, flags);
        if ((flags & BLOCK_BYREF_CALLER) == BLOCK_BYREF_CALLER) {
            if ((flags & BLOCK_FIELD_IS_WEAK) == BLOCK_FIELD_IS_WEAK) {
                _Block_assign_weak(object, destAddr);
            }
            else {
                // do *not* retain or *copy* __block variables whatever they are
                _Block_assign((void *)object, destAddr);
            }
        }
        else if ((flags & BLOCK_FIELD_IS_BYREF) == BLOCK_FIELD_IS_BYREF)  {
            // copying a __block reference from the stack Block to the heap
            // flags will indicate if it holds a __weak reference and needs a special isa
            _Block_byref_assign_copy(destAddr, object, flags);
        }
        // (this test must be before next one)
        else if ((flags & BLOCK_FIELD_IS_BLOCK) == BLOCK_FIELD_IS_BLOCK) {
            // copying a Block declared variable from the stack Block to the heap
            _Block_assign(_Block_copy_internal(object, flags), destAddr);
        }
        // (this test must be after previous one)
        else if ((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT) {
            //printf("retaining object at %p\n", object);
            _Block_retain_object(object);
            //printf("done retaining object at %p\n", object);
            _Block_assign((void *)object, destAddr);
        }
    }
    

    如果block捕获了对象自由变量,在拷贝的时候_Block_object_assign传入的是BLOCK_FIELD_IS_OBJECT,这时候会强引用这个对象,再拷贝地址。

    如果block捕获了__block变量,不管是非对象还是对象,在拷贝时_Block_object_assign传入的是BLOCK_FIELD_IS_BYREF,这时拷贝的是__block变量,把__block变量从栈拷贝到堆。

    __block变量的拷贝同样调用_Block_object_assign ,这时第三个参数为BLOCK_BYREF_CALLER|BLOCK_FIELD_IS_OBJECT

    block对对象的持有是在_Block_object_assign执行时完成的。

    block会强引用__block结构体,而__block结构体强引用对象自由变量。

    __weak修饰符

    void blockStudy()
    {
        dispatch_block_t myBlock;
        {
            NSObject *myObject = [NSObject new];
            __weak NSObject *weakMyObjetc = myObject;
            myBlock = ^() {
                NSLog(@"%@", weakMyObjetc);
            };
        }
    }
    
    // ==>
    
    void blockStudy()
    {
        dispatch_block_t myBlock;
        {
            NSObject *myObject = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new"));
            __attribute__((objc_ownership(weak))) NSObject *weakMyObjetc = myObject;
            myBlock = ((void (*)())&__blockStudy_block_impl_0((void *)__blockStudy_block_func_0, &__blockStudy_block_desc_0_DATA, weakMyObjetc, 570425344));
        }
    }
    
    struct __blockStudy_block_impl_0 {
      struct __block_impl impl;
      struct __blockStudy_block_desc_0* Desc;
      NSObject *__weak weakMyObjetc;
      __blockStudy_block_impl_0(void *fp, struct __blockStudy_block_desc_0 *desc, NSObject *__weak _weakMyObjetc, int flags=0) : weakMyObjetc(_weakMyObjetc) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    static struct __blockStudy_block_desc_0 {
      size_t reserved;
      size_t Block_size;
      void (*copy)(struct __blockStudy_block_impl_0*, struct __blockStudy_block_impl_0*);
      void (*dispose)(struct __blockStudy_block_impl_0*);
    } __blockStudy_block_desc_0_DATA = { 0, sizeof(struct __blockStudy_block_impl_0), __blockStudy_block_copy_0, __blockStudy_block_dispose_0};
    
    static void __blockStudy_block_copy_0(struct __blockStudy_block_impl_0*dst, struct __blockStudy_block_impl_0*src) {_Block_object_assign((void*)&dst->weakMyObjetc, (void*)src->weakMyObjetc, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    

    block捕获变量时把所有权修饰符也捕获了:NSObject *__weak weakMyObjetc,可是拷贝的时候_Block_object_assign传入的同样是3/*BLOCK_FIELD_IS_OBJECT*/,并没有区分?进一步查看_Block_object_assign的汇编代码:

    ___copy_helper_block_:                  ## @__copy_helper_block_
        .cfi_startproc
    ## BB#0:
        pushq   %rbp
    Ltmp6:
        .cfi_def_cfa_offset 16
    Ltmp7:
        .cfi_offset %rbp, -16
        movq    %rsp, %rbp
    Ltmp8:
        .cfi_def_cfa_register %rbp
        subq    $48, %rsp
        movq    %rdi, -8(%rbp)
        movq    %rsi, -16(%rbp)
        movq    -16(%rbp), %rsi
        movq    %rsi, %rdi
        movq    -8(%rbp), %rax
        movq    %rax, %rcx
        addq    $40, %rdi
        movq    %rcx, %rdx
        addq    $40, %rdx
        movq    %rdi, -24(%rbp)         ## 8-byte Spill
        movq    %rdx, %rdi
        movq    -24(%rbp), %rdx         ## 8-byte Reload
        movq    %rsi, -32(%rbp)         ## 8-byte Spill
        movq    %rdx, %rsi
        movq    %rcx, -40(%rbp)         ## 8-byte Spill
        movq    %rax, -48(%rbp)         ## 8-byte Spill
        callq   _objc_copyWeak
        movq    -40(%rbp), %rax         ## 8-byte Reload
        addq    $32, %rax
        movq    -32(%rbp), %rcx         ## 8-byte Reload
        movq    32(%rcx), %rdx
        movq    -48(%rbp), %rsi         ## 8-byte Reload
        movq    $0, 32(%rsi)
        movq    %rax, %rdi
        movq    %rdx, %rsi
        callq   _objc_storeStrong
        addq    $48, %rsp
        popq    %rbp
        retq
        .cfi_endproc
    
        .align  4, 0x90
    

    可以看到它调用了两个方法,分别是_objc_copyWeak_objc_storeStrong,从这里可以看出weak和strong的确是使用了不用的方法对待的。

    相关文章

      网友评论

        本文标题:iOS中Block捕获变量

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