设为首页 加入收藏

TOP

block本质探寻六之修改变量(二)
2019-08-26 07:01:32 】 浏览:63
Tags:block 本质 探寻 修改 变量

//clang

struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding;
 int __flags;
 int __size;
 int age;
};


struct __test3_block_impl_0 {
  struct __block_impl impl;
  struct __test3_block_desc_0* Desc;
  __Block_byref_age_0 *age; // by ref
  __test3_block_impl_0(void *fp, struct __test3_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};


static void __test3_block_func_0(struct __test3_block_impl_0 *__cself) {
  __Block_byref_age_0 *age = __cself->age; // bound by ref

        (age->__forwarding->age) = 20;
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_tb_zgsq5gq15rd3zvbdmw1c09y80000gn_T_main_3fa1c2_mi_2, (age->__forwarding->age));
    }


void test3()
{
    __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};
    block = ((void (*)())&__test3_block_impl_0((void *)__test3_block_func_0, &__test3_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));
}

分析:

<1>在test3()方法中,被__block修饰的age变量被转成__Block_byref_age_0类型的变量,而__Block_byref_age_0是一个结构体并且第一个成员变量是isa指针,那么可以肯定__Block_byref_age_0类型age是一个OC对象——即经__block修饰的auto类型的局部变量会被系统生成一个新的OC对象;

<2>__Block_byref_age_0结构体中:__forwarding是一个指向该结构体本身的指针变量;age就是被捕获到block结构体中的test3()方法中的age(ARC会被copy到堆区);

<3>在block对象的代码块函数__test3_block_func_0中,对整型变量age赋值流程:拿到block对象本身结构体中的成员变量age(__Block_byref_age_0类型指针变量)——>拿到新生成的OC对象结构体__Block_byref_age_0中的成员变量__forwarding——>拿到__Block_byref_age_0中的成员变量age;

补充:_age->__forwarding->age <=> _age->age,但是为什么通过__forwarding(要多一道手续)来拿到最终的整型变量age呢?——该问题后面文章会写到!

 

结论:通过__block修饰auto类型的局部变量来改变值,本质是系统会创建一个临时的OC对象,该对象结构体存储外部变量,而block对象结构体是通过该临时对象来访问外部变量;

补充:

<1> ARC模式强指针持有情况下,该OC临时对象很显然是存放在堆区——否则,test3()方法结束后block回调时,不能正确对age变量赋值(会崩溃)——此处涉及block的内存管理问题,后面文章会写到!

<2>该方法并不会改变局部变量的类型,age其依然是atuo int类型;

<3>__block不能修饰static变量和全局变量

——因为__block就是为了在block代码块中修改外部auto类型的局部变量的值而设计的!

 

三、结论

【1】在block代码块中修改外部auto类型的局部变量的值:用static修饰、设置为全局变量、__block修饰;

【2】static修饰和设置为全局变量弊端:持续占有内存,不利于内存的高效利用;变量的生命周期不可控——__block反之;

【3】__block不能修饰static变量和全局变量;

注:以上对局部实例对象也适用——此处就不再论证了!

 

注:如果在block代码块中对可变数组执行添加数组元素的操作,不需要用__block修饰数组指针,因为添加元素并不是改变数组的值(具体指数组指针的地址);

 

四、拓展——查找age地址值

//代码

void test4()
{
    __block int age = 10;
    block = ^{
        age = 20;
        NSLog(@"%d", age);
    };
    
    NSLog(@"%p", &age);
}

//打印

2019-01-15 17:58:15.930081+0800 MJ_TEST[5583:308759] 0x100701f38
2019-01-15 17:58:15.930453+0800 MJ_TEST[5583:308759] 20
Program ended with exit code: 0

分析:打印出的age地址,据上述分析,到底是新生成的oc对象本身的地址,还是该对象结构体内成员变量age的值?

//代码

struct __Block_byref_age_0 {
    void *__isa;
    struct __Block_byref_age_0 *__forwarding;
    int __flags;
    int __size;
    int age;
};

struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};

struct __test3_block_desc_0 {
    size_t reserved;
    size_t Block_size;
    void (*copy)(void);
    void (*dispose)(void);
};

struct _
首页 上一页 1 2 3 下一页 尾页 2/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇iOS学习——页面的传值方式 下一篇TCP\UDP客户—服务器程序设计基本..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目