设为首页 加入收藏

TOP

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

说明:

<1>阅读本文章,请参照前面的block文章加以理解;

<2>本文的变量指的是auto类型的局部变量(包括实例对象);

<3>ARC和MRC两种模式均适用;

一、无法修改的原因

//代码

 

很明显,强行给age赋值会报错;

void test1()
{
    int age = 10;
    block = ^{
//        age = 20;
        NSLog(@"%d", age);
    };
}

//打印

2019-01-15 15:00:43.641417+0800 MJ_TEST[3676:199449] 10
Program ended with exit code: 0

分析:为什么在block内部不能改变age的值?往下看

//clang

struct __test1_block_impl_0 {
  struct __block_impl impl;
  struct __test1_block_desc_0* Desc;
  int age;
  __test1_block_impl_0(void *fp, struct __test1_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};


static void __test1_block_func_0(struct __test1_block_impl_0 *__cself) {
  int age = __cself->age; // bound by copy


        NSLog((NSString *)&__NSConstantStringImpl__var_folders_tb_zgsq5gq15rd3zvbdmw1c09y80000gn_T_main_d8e7e4_mi_0, age);
    }


void test1()
{
    int age = 10;
    block = ((void (*)())&__test1_block_impl_0((void *)__test1_block_func_0, &__test1_block_desc_0_DATA, age));
}

分析:

<1>age被捕捉到block结构体中,根据输出的结果很明显是在ARC模式下,因此当被强指针变量block持有时,系统会自动将block对象从栈区拷贝到堆区;而MRC模式下,因为block对象会随着test1()方法结束,其内存地址会被回收,age的值为乱码

2019-01-15 16:12:43.234301+0800 MJ_TEST[4434:243013] -272632456
Program ended with exit code: 0

<2>block代码块是通过__test1_block_func_0函数来实现,而该函数应用的age就是block对象结构体__test1_block_impl_0中的age,这跟test1()方法中的age是两个不同的age:可以通过打印两个age地址发现,他们的地址确实不一样,如果是ARC,前者存在于堆区,后者存在于栈区;而MRC,都在栈区,但内存地址不一样;

<3>想要在__test1_block_impl_0函数中去改变test1()方法中的局部变量,显然是不成立的,根本就拿不到该局部变量;

但为什么修改内部age会报错?

        苹果设计的初衷就是要保持内外部变量的一致性即同一个变量(有利于程序员的理解),__block修饰就是实现这个一致性,后面查找age地址验证会提到!

 

二、修改方法

1)static修饰

//代码

void test2()
{
    static int age = 10;
    block = ^{
        age = 20;
        NSLog(@"%d", age);
    };
}

//打印

2019-01-15 16:31:56.533685+0800 MJ_TEST[4676:255859] 20
Program ended with exit code: 0

//clang

struct __test2_block_impl_0 {
  struct __block_impl impl;
  struct __test2_block_desc_0* Desc;
  int *age;
  __test2_block_impl_0(void *fp, struct __test2_block_desc_0 *desc, int *_age, int flags=0) : age(_age) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};



static void __test2_block_func_0(struct __test2_block_impl_0 *__cself) {
  int *age = __cself->age; // bound by copy

        (*age) = 20;
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_tb_zgsq5gq15rd3zvbdmw1c09y80000gn_T_main_bf7285_mi_1, (*age));
    }

分析:

<1>根据前述文章,此时test2()方法中的整型age是以指针的形式被捕捉到block对象结构体中,该指针变量指向值为10的内存区域;

<2>通过指针当然可以变量该指针指向的内存区域的值(见“__test2_block_func_0”函数),这点没问题——C语言语法基础;

结论:通过static修饰auto类型的局部变量来改变值,其本质是通过指针来改变变量的值;

补充:static修饰的弊端

<1>修改了变量的属性类型——age由整型变量变成整型指针变量;

<2>static修饰的局部变量,是存放在数据区(全局区),直到整个程序结束才会释放内存——不利于内存的有效利用;

 

2)设置为全局变量

此处就不论证,很容易理解,block对象代码块是放在另一个函数中,而该函数是可以访问该全局变量的——这点没问题;

 

3)__block修饰

//代码

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

//打印

2019-01-15 16:51:56.337321+0800 MJ_TEST[4909:268533] 20
Program ended with exit code: 0
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇iOS学习——页面的传值方式 下一篇TCP\UDP客户—服务器程序设计基本..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目