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

本文探讨了__block修饰的本地变量在MRC和ARC环境下与Block的关系及变化。分析了为何在Block中能修改__block变量,以及Block内外变量地址的变化情况。总结了在不同内存管理环境下,Block捕获变量的复制行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

__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类型的。

``` typedef struct hal_dma_lli { void* src_addr; void* dst_addr; struct hal_dma_lli *next; reg_dma_ctll_t lli_ctl_l; reg_dma_ctlh_t lli_ctl_h; uint32_t dstat; uint32_t sstat; uint32_t block_size; }hal_dma_lli_t; hal_status_e hal_dma_channel_tf_ll_set(uint8_t channel_num, hal_dma_lli_t *tf_ll) { uint32_t i = 0; reg_dma_ctll_t reg_ch_ctl_l; hal_dma_lli_t *tf_lli_tmp = tf_ll; reg_dma_llpl_t reg_llp_l; if (channel_num >= HAL_DMA_CHANNEL_NUM) { return E_HAL_ERROR_OUT_OF_RANGE; } if (NULL == tf_ll) { return E_HAL_ERROR_PARAMETER; } // transfer is not complete,can&#39;t modify cfg. if (E_HAL_OK != hal_dma_channel_valid(channel_num)) { return E_HAL_ERROR_RESOURCE; } dma.channels[channel_num].tf_ll_head = tf_ll; reg_llp_l.lms = 0; reg_llp_l.loc = (uint32_t)tf_ll >> 2; hal_write_reg32(HAL_DMA_BASE_ADDR + REG_DMA_LLPL_OFFSET(channel_num), reg_llp_l); while (tf_lli_tmp && (i < dma.channels[channel_num].user_cfg.block_total)) { hal_read_reg32(HAL_DMA_BASE_ADDR + REG_DMA_CTLL_OFFSET(channel_num), reg_ch_ctl_l); tf_lli_tmp->lli_ctl_l.int_en = reg_ch_ctl_l.int_en; tf_lli_tmp->lli_ctl_l.dst_tr_width = dma.channels[channel_num].user_cfg.dst_tr_width; tf_lli_tmp->lli_ctl_l.src_tr_width = dma.channels[channel_num].user_cfg.src_tr_width; tf_lli_tmp->lli_ctl_l.dinc = dma.channels[channel_num].user_cfg.dst_modify_mode; tf_lli_tmp->lli_ctl_l.sinc = dma.channels[channel_num].user_cfg.src_modify_mode; tf_lli_tmp->lli_ctl_l.dest_msize = dma.channels[channel_num].user_cfg.dst_burst_size; tf_lli_tmp->lli_ctl_l.dest_msize = dma.channels[channel_num].user_cfg.src_burst_size; tf_lli_tmp->lli_ctl_l.tt_fc = dma.channels[channel_num].user_cfg.tf_fc; tf_lli_tmp->lli_ctl_l.dms = 0; tf_lli_tmp->lli_ctl_l.sms = 0; switch (dma.channels[channel_num].user_cfg.tf_type) { case E_DMA_CONT_TO_LINKLIST: case E_DMA_RELOAD_TO_LINKLIST: tf_lli_tmp->lli_ctl_l.llp_src_en = 0; tf_lli_tmp->lli_ctl_l.llp_dst_en = 1; break; case E_DMA_LINKLIST_TO_CONT: case E_DMA_LINKLIST_TO_RELOAD: tf_lli_tmp->lli_ctl_l.llp_src_en = 1; tf_lli_tmp->lli_ctl_l.llp_dst_en = 0; break; case E_DMA_LINKLIST_TO_LINKLIST: tf_lli_tmp->lli_ctl_l.llp_src_en = 1; tf_lli_tmp->lli_ctl_l.llp_dst_en = 1; break; default: break; } tf_lli_tmp->lli_ctl_h.block_ts = tf_lli_tmp->block_size; tf_lli_tmp->lli_ctl_h.done = 0; tf_lli_tmp->lli_ctl_h.rsv0 = 0; tf_lli_tmp = tf_lli_tmp->next; i++; } return E_HAL_OK; }```请解释这段代码,分析出不规范的地方和待改进的地方并改进。
03-21
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值