NO
第六章 塊與大中樞派發(fā)(GCD)
37. 塊的基本概念
- 塊的強大之處:在聲明它的范圍里所有變量都可以為其所捕獲. 默認情況下,為塊所捕獲的變量,是不可以在塊里修改的.若聲明變量的時候加上__block修飾符,就可以在塊內修改了.
- 塊可以分配在椘暄或堆上,?也可以是全局的.分配在棧上的塊可以copy到堆上,一旦拷貝到堆上,塊就成了帶引用計數的對象了.后續(xù)的復制都不會真的執(zhí)行復制,只是遞增塊對象的引用計數
- 全局塊:不會捕捉任何狀態(tài),運行時也無須有狀態(tài)來參與.塊所使用的整個內存在編譯期就已經完全確定了.屬于一種優(yōu)化操作
void (^block)() = ^{
NSLog(@"全局塊");
};
- 如果將塊定義在實例方法中,那么除了可以訪問類的所有實例變量外,還可以使用self變量.塊總能修改實例變量,所以在聲明時無需添加__block.不過如果通過讀取或寫入操作捕獲了實例變量,那么也會自動將self變量一并捕獲,因為實例變量是與self所指代的實例關聯在一起的.
- self也是個對象,因而塊在捕獲它時,也會將其保留,如果self所指代的對象同時也保留了塊,那么會導致保留環(huán).
塊的內部結構
-
塊的內存布局:塊本身也是個對象,在存放的內存區(qū)域中,首個變量是指向Class對象的指針.其余內存里含有塊對象正常運轉所需的各種信息
塊的內部結構.png - 在內存布局中,最重要的就是invoke變量,這是個函數指針,指向塊的實現代碼.函數原型至少要接受一個void* 型的參數,此參數代表塊. 塊其實就是一種代替函數指針的語法結構,原來使用函數指針時,需要用"不透明的void指針"來傳遞狀態(tài),而改用塊后則可以把原來用C語言所編寫的代碼封裝成簡明易用的接口.
- descriptor變量是指向結構體的指針,每個塊里都包含此結構體,其中聲明了塊對象的總體大小,還聲明了copy和dispose兩個輔助函數所對應的函數指針.輔助函數在拷貝及丟棄塊對象是運行,比如copy要保留捕獲的對象,dispose則將之釋放.
- 塊還會把它所捕獲的所有變量都拷貝一份.這些拷貝放在descriptor變量后面,捕獲多少個變量,就要占據多少內存空間.請注意拷貝的不是對象本身,而是指向這些對象的指針變量.