面试

iOS中Block捕获变量

2020-01-17  本文已影响0人  HoooChan

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的确是使用了不用的方法对待的。

上一篇下一篇

猜你喜欢

热点阅读