示例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
发现不加__block
,block
内部会重新生成了一变量。示例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
中:对比结果1
和3
打印的地址,尽管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
只是告诉编译器可以修改重新生成的变量。
最后:
由于本人目前知识储备有限,以上说明中,也许会存在一些错误的描述和认识,还希望发现后你能及时指出,以免误导其他读者,在此谢过。