牛骨文教育服务平台(让学习变的简单)
博文笔记

iOS不使用__block修改Block中的局部变量<指针篇>

创建时间:2017-05-14 投稿人: 浏览次数:991

FromBaidu.jpg

Laziness is like a lock, which bolts you out of the storehouse of information and makes you an intellectual starveling. –Bernard Shaw
懒惰就象一把枷锁,锁住了知识的仓库,使你的智力便得匮乏。–萧伯纳

示例1:

    int a = 10;
    NSLog(@"1---%p",&a);
    NSLog(@"1---%d",a);
    [UIView animateWithDuration:1 animations:^{
        NSLog(@"2--->:%p",&a);
        NSLog(@"2--->:%d",a);
    }];
    NSLog(@"3--->%p",&a);
    NSLog(@"3--->%d",a);
  • 结果:
    示例1.png

示例2:

    __block int a = 10;
    NSLog(@"1---%p",&a);
    NSLog(@"1---%d",a);
    [UIView animateWithDuration:1 animations:^{
        NSLog(@"2--->:%p",&a);
        NSLog(@"2--->:%d",a);
    }];
    NSLog(@"3--->%p",&a);
    NSLog(@"3--->%d",a);
  • 结果:

    示例2.png

  • 结论:

    对比示例1示例2发现不加__blockblock内部会重新生成了一变量。示例1中生成的变量依然在栈区,地址减4,不加__block之所以不能修改局部变量是因为编译器加了限制,默认不允许修改。而以上说明,并没有脱离变量a的作用域,详见执行顺序。因为[UIView animateWithDuration:中的block会立即执行,这也是为何不用weakSelf不会造成循环引用的原因。下面我们使用GCD,演示超过作用域的情况。


示例3:
    int a = 10;
    NSLog(@"1---%p",&a);
    NSLog(@"1---%d",a);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"2--->:%p",&a);
        NSLog(@"2--->:%d",a);
    });
    NSLog(@"3--->%p",&a);
    NSLog(@"3--->%d",a);
  • 结果:
    示例3.png

示例4:

    __block int a = 10;
    NSLog(@"1---%p",&a);
    NSLog(@"1---%d",a);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"2--->:%p",&a);
        NSLog(@"2--->:%d",a);
    });
    NSLog(@"3--->%p",&a);
    NSLog(@"3--->%d",a);
  • 结果:
    示例4.png
  • 结论:

    对比示例3示例4发现超过作用域生成的变量在堆区。有趣的是在示例4中:对比结果13打印的地址,尽管GCD没有执行,而a的地址已经发生了改变,也就是说在编译时a的地址已经发生了改变。接下来,我们将直接访问地址,来修改block中访问的“局部变量”,而不需要加__block.


示例5:
    int a = 10;
    NSLog(@"1---%p",&a);
    NSLog(@"1---%d",a);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        int *b = (int *)&a;
        *b = 20;
        NSLog(@"2--->:%p",&a);
        NSLog(@"2--->:%d",a);
    });
    NSLog(@"3---%p",&a);
    NSLog(@"3---%d",a);
  • 结果:
    示例5.png
  • 结论
    不加__block,在block中间接修改所谓的“局部变量”,实际上也是重新生成的局部变量,只是名字相同而已,超过作用域的变量a已经释放了,而使用指针只不过是绕开编译器的检查,间接通过地址修改内容。同理加上__block只是告诉编译器可以修改重新生成的变量。

我的上一篇文章:炫酷悬停交互视图

最后:

由于本人目前知识储备有限,以上说明中,也许会存在一些错误的描述和认识,还希望发现后你能及时指出,以免误导其他读者,在此谢过。

声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。