Block不允許修改外部變量的值,這里所說(shuō)的外部變量的值,指的是棧中指針的內(nèi)存地址。__block所起到的作用就是只要觀(guān)察到該變量被 block 所持有,就將“外部變量”在棧中的內(nèi)存地址放到了堆中。進(jìn)而在block內(nèi)部也可以修改外部變量的值。---這是 微博@唐巧_boy的《iOS開(kāi)發(fā)進(jìn)階》中的第11.2.3章節(jié)中的描述。
簡(jiǎn)單數(shù)據(jù)類(lèi)型局部變量

- 在這里a是局部變量,所以是放在棧當(dāng)中的。block相當(dāng)于另一個(gè)內(nèi)部函數(shù),要在另一個(gè)作用域中改變棧中指針的內(nèi)存地址是不行的。此時(shí),如果block想要在內(nèi)部進(jìn)行操作就要將a變量拷貝到堆中,程序才能去改變。
- 在block中再次打印a地址,可以發(fā)現(xiàn)地址不一樣了。這就是__block的作用,它將棧中的變量a進(jìn)行copy放到堆上。(此時(shí)輸出的是堆地址)
- 0x7開(kāi)頭的是在棧上的地址。0x1開(kāi)頭的為堆上地址。
- 在調(diào)用block后,a變量就已經(jīng)是新的內(nèi)存地址,也不再是棧上。
- 這里如果不加__block,a = 3就是修改a指針的內(nèi)存地址。這樣是不行的。
不可變對(duì)象局部變量

- NSString為不可變字符串,初始化后不能修改內(nèi)容(這里內(nèi)容指的是堆上開(kāi)辟的空間內(nèi)容)。不加__block的情況下,string指針引用永遠(yuǎn)為初始化時(shí)的內(nèi)容地址(也就是永遠(yuǎn)指向@“dddd”)。
- 加上__block后,在block內(nèi)部會(huì)將string從棧copy到堆上。這樣我們就可以操作string的指針內(nèi)存地址,進(jìn)行修改。也就是可以 string = @“fffff”的操作。
- 可以看出block以后,string的所有都copy到堆中。(特別說(shuō)明:一個(gè)對(duì)象的生成,會(huì)在棧中開(kāi)辟指針地址,指針地址中存放一個(gè)堆地址,這個(gè)地址就是真正存放對(duì)象內(nèi)容的區(qū)域。也就是說(shuō)對(duì)象都是放在堆上的。)
可變對(duì)象局部變量
<p>(1)添加__block情況</p>
<pre><code>
` int b = 1;
__block NSMutableString *a = [NSMutableString stringWithString:@"Tom"];
NSLog(@"開(kāi)始%@",a);
NSLog(@"b的地址為棧地址開(kāi)頭%p",&b);
NSLog(@"\n 定以前:------------------------------------\n
a指向的堆中地址:%p;a在棧中的指針地址:%p", a, &a); //a在棧區(qū)
void (^foo)(void) = ^{
a.string = @"Jerry";
NSLog(@"\n block內(nèi)部:------------------------------------\n\
a指向的堆中地址:%p;a在棧中的指針地址:%p", a, &a); //a在棧區(qū)
a = [NSMutableString stringWithString:@"William"];
NSLog(@"block內(nèi)部%@",a);
};
foo();
NSLog(@"\n 定以后:------------------------------------\n\
a指向的堆中地址:%p;a在棧中的指針地址:%p", a, &a); //a在棧區(qū)
NSLog(@"最后%@",a);`
</code>
</pre>

- 可以發(fā)現(xiàn)經(jīng)過(guò)block后,將a的內(nèi)容全部都copy到堆上了。
- a = [NSMutableString stringWithString:@"William"]; 就是更改了a指針地址內(nèi)容。
<p>(2)不添加__block情況</p>
<pre><code>
` int b = 1;
NSMutableString *a = [NSMutableString stringWithString:@"Tom"];
NSLog(@"開(kāi)始%@",a);
NSLog(@"b的地址為棧地址開(kāi)頭%p",&b);
NSLog(@"\n 定以前:------------------------------------\n
a指向的堆中地址:%p;a在棧中的指針地址:%p", a, &a); //a在棧區(qū)
void (^foo)(void) = ^{
a.string = @"Jerry";
NSLog(@"\n block內(nèi)部:------------------------------------\n\
a指向的堆中地址:%p;a在棧中的指針地址:%p", a, &a); //a在棧區(qū)
// a = [NSMutableString stringWithString:@"William"];
NSLog(@"block內(nèi)部%@",a);
};
foo();
NSLog(@"\n 定以后:------------------------------------\n\
a指向的堆中地址:%p;a在棧中的指針地址:%p", a, &a); //a在棧區(qū)
NSLog(@"最后%@",a);`
</code>
</pre>

- 可變對(duì)象可以不加__block情況下,直接修改內(nèi)容。
- a.string = @"Jerry" 修改的是堆上的內(nèi)容。并不是a在棧上的指針地址。
- 所以最后a在堆上地址和a在棧上地址并沒(méi)有改變。改變的只是a在堆上地址指向的內(nèi)容。
參考iOS中__block 關(guān)鍵字的底層實(shí)現(xiàn)原理自己總結(jié)的內(nèi)容,如果有哪里有錯(cuò)誤還請(qǐng)大家指教。