Block不允许修改外部变量的值,这里所说的外部变量的值,指的是栈中指针的内存地址。__block所起到的作用就是只要观察到该变量被 block 所持有,就将“外部变量”在栈中的内存地址放到了堆中。进而在block内部也可以修改外部变量的值。---这是 微博@唐巧_boy的《iOS开发进阶》中的第11.2.3章节中的描述。
简单数据类型局部变量
![](http://upload-images.jianshu.io/upload_images/2043628-eba1784bd477ee46.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/584)
- 在这里a是局部变量,所以是放在栈当中的。block相当于另一个内部函数,要在另一个作用域中改变栈中指针的内存地址是不行的。此时,如果block想要在内部进行操作就要将a变量拷贝到堆中,程序才能去改变。
- 在block中再次打印a地址,可以发现地址不一样了。这就是__block的作用,它将栈中的变量a进行copy放到堆上。(此时输出的是堆地址)
- 0x7开头的是在栈上的地址。0x1开头的为堆上地址。
- 在调用block后,a变量就已经是新的内存地址,也不再是栈上。
- 这里如果不加__block,a = 3就是修改a指针的内存地址。这样是不行的。
不可变对象局部变量
![](http://upload-images.jianshu.io/upload_images/2043628-038d058b4d33109a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
- NSString为不可变字符串,初始化后不能修改内容(这里内容指的是堆上开辟的空间内容)。不加__block的情况下,string指针引用永远为初始化时的内容地址(也就是永远指向@“dddd”)。
- 加上__block后,在block内部会将string从栈copy到堆上。这样我们就可以操作string的指针内存地址,进行修改。也就是可以 string = @“fffff”的操作。
- 可以看出block以后,string的所有都copy到堆中。(特别说明:一个对象的生成,会在栈中开辟指针地址,指针地址中存放一个堆地址,这个地址就是真正存放对象内容的区域。也就是说对象都是放在堆上的。)
可变对象局部变量
<p>(1)添加__block情况</p>
<pre><code>
` int b = 1;
__block NSMutableString *a = [NSMutableString stringWithString:@"Tom"];
NSLog(@"开始%@",a);
NSLog(@"b的地址为栈地址开头%p",&b);
NSLog(@"\n 定以前:------------------------------------\n
a指向的堆中地址:%p;a在栈中的指针地址:%p", a, &a); //a在栈区
void (^foo)(void) = ^{
a.string = @"Jerry";
NSLog(@"\n block内部:------------------------------------\n\
a指向的堆中地址:%p;a在栈中的指针地址:%p", a, &a); //a在栈区
a = [NSMutableString stringWithString:@"William"];
NSLog(@"block内部%@",a);
};
foo();
NSLog(@"\n 定以后:------------------------------------\n\
a指向的堆中地址:%p;a在栈中的指针地址:%p", a, &a); //a在栈区
NSLog(@"最后%@",a);`
</code>
</pre>
![](http://upload-images.jianshu.io/upload_images/2043628-b5159562874660aa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/582)
- 可以发现经过block后,将a的内容全部都copy到堆上了。
- a = [NSMutableString stringWithString:@"William"]; 就是更改了a指针地址内容。
<p>(2)不添加__block情况</p>
<pre><code>
` int b = 1;
NSMutableString *a = [NSMutableString stringWithString:@"Tom"];
NSLog(@"开始%@",a);
NSLog(@"b的地址为栈地址开头%p",&b);
NSLog(@"\n 定以前:------------------------------------\n
a指向的堆中地址:%p;a在栈中的指针地址:%p", a, &a); //a在栈区
void (^foo)(void) = ^{
a.string = @"Jerry";
NSLog(@"\n block内部:------------------------------------\n\
a指向的堆中地址:%p;a在栈中的指针地址:%p", a, &a); //a在栈区
// a = [NSMutableString stringWithString:@"William"];
NSLog(@"block内部%@",a);
};
foo();
NSLog(@"\n 定以后:------------------------------------\n\
a指向的堆中地址:%p;a在栈中的指针地址:%p", a, &a); //a在栈区
NSLog(@"最后%@",a);`
</code>
</pre>
![](http://upload-images.jianshu.io/upload_images/2043628-0f61621fa2c93167.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/610)
- 可变对象可以不加__block情况下,直接修改内容。
- a.string = @"Jerry" 修改的是堆上的内容。并不是a在栈上的指针地址。
- 所以最后a在堆上地址和a在栈上地址并没有改变。改变的只是a在堆上地址指向的内容。
作者:i大猫哥
链接:https://www.jianshu.com/p/a41396ff0247
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。