您的当前位置:首页正文

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

来源:华拓网
示例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只是告诉编译器可以修改重新生成的变量。

最后:

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