Block不允許修改外部變量的值缓升,這里所說的外部變量的值申鱼,指的是棧中指針的內(nèi)存地址挺勿。__block 所起到的作用就是只要觀察到該變量被 block 所持有讶坯,就將“外部變量”在棧中的內(nèi)存地址放到了堆中续搀。進(jìn)而在block內(nèi)部也可以修改外部變量的值嵌洼。
Block不允許修改外部變量的值篡石。Apple這樣設(shè)計属提,應(yīng)該是考慮到了block的特殊性寂汇,block也屬于“函數(shù)”的范疇病往,變量進(jìn)入block,實際就是已經(jīng)改變了作用域骄瓣。在幾個作用域之間進(jìn)行切換時停巷,如果不加上這樣的限制,變量的可維護(hù)性將大大降低榕栏。又比如我想在block內(nèi)聲明了一個與外部同名的變量畔勤,此時是允許呢還是不允許呢?只有加上了這樣的限制扒磁,這樣的情景才能實現(xiàn)庆揪。于是棧區(qū)變成了紅燈區(qū),堆區(qū)變成了綠燈區(qū)妨托。
我們可以打印下內(nèi)存地址來進(jìn)行驗證:
__block int a = 0;
NSLog(@"定義前:%p", &a); //棧區(qū)
void (^foo)(void) = ^{
a = 1;
NSLog(@"block內(nèi)部:%p", &a); //堆區(qū)
};
NSLog(@"定義后:%p", &a); //堆區(qū)
foo();
2016-05-17 02:03:33.559 LeanCloudChatKit-iOS[1505:713679] 定義前:0x16fda86f8
2016-05-17 02:03:33.559 LeanCloudChatKit-iOS[1505:713679] 定義后:0x155b22fc8
2016-05-17 02:03:33.559 LeanCloudChatKit-iOS[1505:713679] block內(nèi)部: 0x155b22fc8
“定義后”和“block內(nèi)部”兩者的內(nèi)存地址是一樣的缸榛,我們都知道 block 內(nèi)部的變量會被 copy 到堆區(qū),“block內(nèi)部”打印的是堆地址兰伤,因而也就可以知道内颗,“定義后”打印的也是堆的地址。
那么如何證明“block內(nèi)部”打印的是堆地址医清?
把三個16進(jìn)制的內(nèi)存地址轉(zhuǎn)成10進(jìn)制就是:
定義后前:6171559672
block內(nèi)部:5732708296
定義后后:5732708296
中間相差438851376個字節(jié)起暮,也就是 418.5M 的空間,因為堆地址要小于棧地址会烙,又因為iOS中一個進(jìn)程的棧區(qū)內(nèi)存只有1M负懦,Mac也只有8M,顯然a已經(jīng)是在堆區(qū)了柏腻。
這也證實了:a 在定義前是棧區(qū)纸厉,但只要進(jìn)入了 block 區(qū)域,就變成了堆區(qū)五嫂。這才是 __block 關(guān)鍵字的真正作用颗品。
__block 關(guān)鍵字修飾后肯尺,int類型也從4字節(jié)變成了32字節(jié),這是 Foundation 框架 malloc 出來的躯枢。這也同樣能證實上面的結(jié)論则吟。(PS:居然比 NSObject alloc 出來的 16 字節(jié)要多一倍)。
理解到這是因為堆棧地址的變更锄蹂,而非所謂的“寫操作生效”氓仲,這一點至關(guān)重要
Block不允許修改外部變量的值,這里所說的外部變量的值得糜,指的是棧中指針的內(nèi)存地址敬扛。棧區(qū)是紅燈區(qū),堆區(qū)才是綠燈區(qū)