block(3) - 变量与__block修饰符

__block修饰变量的时候时候

1、__block修饰本地变量转为cpp的样子

    //用__block修饰的本地变量b多了这个结构体
    struct __Block_byref_b_0 {      //直译为本地变量b在block中是通过引用的
      void *__isa;             
    __Block_byref_b_0 *__forwarding;     
     int __flags;
     int __size;
     int b;
    };
    //此函数的作用是将栈上的b copy 到堆中
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) 
    {
        _Block_object_assign((void*)&dst->b, (void*)src->b, 8/*BLOCK_FIELD_IS_BYREF*/);
    }

    //此函数的作用是将栈上的b 释放
    static void __main_block_dispose_0(struct __main_block_impl_0*src) 
    {
        _Block_object_dispose((void*)src->b, 8/*BLOCK_FIELD_IS_BYREF*/);
    }
static struct __main_block_desc_0 {    //描述block的信息
  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};
    //可以看到b在block中是这个样子的
static int __main_block_func_0(struct __main_block_impl_0 *__cself) {

  __Block_byref_b_0 *b = __cself->b; // bound by ref       //这里是通过引用来修改的
     ......
   }
struct __main_block_impl_0 {      //需要填充的结构体
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;

    .....
    //在要填充的结构体中
    __Block_byref_b_0 *b; // by ref       //多了一个属性

    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_b_0 *_b, int flags=0) :b(_b->__forwarding) {
    ......
  }
};
    int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ 
    { __AtAutoreleasePool __autoreleasepool; 

        __attribute__((__blocks__(byref))) __Block_byref_b_0 b = {(void*)0,(__Block_byref_b_0 *)&b, 0, sizeof(__Block_byref_b_0), 20};
        //可以看到被__block修饰的静态局部变量被转换成了这样
        //__attribute__设置了一个block引用的属性

        ......
        //填充此结构体
        int (*block)(void) = ((int (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA,(__Block_byref_b_0 *)&b, 570425344));

        ......
        //调用结构体
        ((int (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block)

        ......
    }
    return 0;
}

2、本地变量与block的关系及其变化

1、MRC情况下

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        __block int b = 20;

        NSLog(@"&b:%p ,b:%d",&b,b);
        //&b:0x7ffeefbf7168 ,b:20

        int (^block)(void) = ^(void)
        {
            NSLog(@"&b:%p ,b:%d",&b,b);
            b = 100;    //疑问 1:为什么能在block中修改__block修饰的本地变量b?
            return b;
        };

        NSLog(@"&b:%p ,b:%d",&b,b);
        //&b:0x7ffeefbf7168 ,b:20

        NSLog(@"%d",block());
        //&b:0x7ffeefbf7168 ,b:20
        //100

        //疑问 2:为什么在MRC中,被__block修饰的本地变量的地址在block内外都没有发生变化?
    }
    return 0;
}

2、MRC情况下,将block作copy

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        __block int b = 20;

        NSLog(@"&b:%p ,b:%d",&b,b);
        //&b:0x7ffee2e79168 ,b:20

        int (^block)(void) = [^(void)
        {
            NSLog(@"&b:%p ,b:%d",&b,b);
            b = 100;    //疑问 1:为什么能在block中修改__block修饰的本地变量b?
            return b;
        }copy];    //注意这里进行了copy

        NSLog(@"&b:%p ,b:%d",&b,b);
        //&b:0x60000003ce78 ,b:20

        NSLog(@"%d",block());
        //&b:0x60000003ce78 ,b:20
        //100

        //疑问 3:为什么在MRC中,将block作copy后的地址就改变了?
    }
    return 0;
}

3、ARC下

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        __block int b = 20;

        NSLog(@"&b:%p ,b:%d",&b,b);
        //&b:0x7ffee2e79168 ,b:20

        int (^block)(void) = ^(void)
        {
            NSLog(@"&b:%p ,b:%d",&b,b);
            b = 100;    //疑问 1:为什么能在block中修改__block修饰的本地变量b?
            return b;
        };    //注意这里没有进行copy

        NSLog(@"&b:%p ,b:%d",&b,b);
        //&b:0x60000003ce78 ,b:20

        NSLog(@"%d",block());
        //&b:0x60000003ce78 ,b:20
        //100

        //疑问 4:为什么在ARC中,将block不作copy,block内外的地址都能改变?    
    return 0;
}

疑问 1:为什么能在block中修改__block修饰的本地变量b?
- 因为b转换为__Block_byref_b_0结构体类型后,将地址传引用进去了。

疑问 2:为什么在MRC中,被__block修饰的本地变量b的地址在block内外都没有发生变化?
- 在1中,很明显此block是一个__NSStackBlock,而且b传的是地址进block,通过访问此地址输出的b的地址自然都是一样。

疑问 3:为什么在MRC中,将block作copy后的地址就改变了?
- **在2中,将__NSStackBlock进行copy后变成了一个__NSMallocBlock,多以对于__block修饰的本地变量b也copy了一份到堆上(通过__main_block_copy_0函数),而将原来的栈上的本地变量b释放了(通过__main_block_dispose_0()函数),得到的就是新的地址了。此过程发生在初始化block时候。

疑问 4:为什么在ARC中,将block不作copy,block内外的地址都能改变?
- 在3中,因为将block进行 = 操作了,所以在ARC中会导致调用objc_retainBlock->Block_copy->_Block_copy_internal方法链。并导致 __NSStackBlock_ 类型的 block 转换为 _NSMallocBlock_ 类型。自然也就得到疑问 3的答案了

总结:

MRC中:只有手动copy,block所捕获的东西才会复制到堆上,block也是__NSStackBlock类型的。__forwarding指针也只指向自己。

ARC中:只要将block进行 = ,block所捕获的东西就会复制到堆上,Block也是__NSMallocBlock类型的。相反它要是不 进行block = ,那么block也是__NSStackBlock类型的。

你可能感兴趣的:(oc)