int multiplier = 6
int(^ Block)(int) = ^int(int num){
return num* multiplier
};
Block(2);
Block
"Block是什么"
是將函數(shù)及其執(zhí)行上下文封裝起來的對象
內部有isa指針和FuncPtr函數(shù)指針
isa說明他是個對象,FuncPtr指針指向了函數(shù)實現(xiàn)
"Block調用是什么"
就是函數(shù)調用
當我們調用block(2)時,內部實現(xiàn)是
通過block結構體里面的函數(shù)指針,取出對應的執(zhí)行體.將參數(shù)傳遞進來(block本身,2),然后進行內部調用
源碼分析
"Block源碼結構體"中包括以下內容,說明了Block為什么是對象以及是怎么將函數(shù)和執(zhí)行上下文封裝的
-block_impl結構體
isa指針
FuncPtr函數(shù)指針,指向我們在block花括號中的執(zhí)行體
-block相關描述的結構體
-block中傳進來的參數(shù)(局部變量)
"截獲變量的特性的內部實現(xiàn)"
最上面的示例中,當截獲了變量multiplier,是把它傳到上述的block結構體中
注意??:一旦在block結構體中賦值了,再操作時就是對結構體中的變量操作了,不是對block外的變量操作了
若傳進來:
局部變量基本數(shù)據(jù)類型 - 在block結構體中,直接截獲這個值,賦值給block內部使用
局部變量對象類型(不知道為啥成員變量也截獲) - 在block結構體中,連同對象的修飾符一起截獲,賦值給block內部使用
注意??:block的循環(huán)引用,就是因為局部對象是聯(lián)通修飾符一起截獲的
靜態(tài)局部變量 - 在block結構體中,以指針形式截獲,也就是說,如果在block的定義之后,對靜態(tài)局部變量值進行修改,再調用block時,用的是最新的值
全局變量 - 不在block結構體中
靜態(tài)全局變量 - 不在block結構體中
"截獲變量總結"
局部變量基本數(shù)據(jù)類型 - 直接截獲,傳進來是什么就是什么,不會改變
局部變量對象類型 - 連同所有權修飾符一起截獲
靜態(tài)局部變量 -指針截獲,值會改變
全局變量 -不截獲,值會改變
靜態(tài)全局變量 -不截獲,值會改變

截獲變量示例
__block修飾符
對截獲變量進行賦值操作時需要添加__block
注意??:賦值不等于使用!!!
array = [NSMutableArray array]就是賦值,若在block內部調用的話,需要為外部的array聲明添加__block修飾符
[array addObject:@!23]就是使用而不是賦值
"如何使用__block修飾符"
"以下變量的賦值操作,需要使用__block修飾的情況"
添加__block之后,當外部變量值改了之后,block內部調用時也會更改
1. 在block內部對局部變量基本數(shù)據(jù)類型進行賦值操作時
2. 在block內部對局部變量對象數(shù)據(jù)類型進行賦值操作時
"以下變量的賦值操作,不需要__block修飾"
1. 在block內部對靜態(tài)局部變量進行賦值操作時
2. 在block內部對全局變量進行賦值操作時
3. 在block內部對靜態(tài)全局變量進行賦值操作時
因為全局變量和靜態(tài)全局變量都不涉及截獲操作
靜態(tài)局部變量是通過指針來使用的,操作的是block外部的變量,所以不需要修飾
"__block做了什么"
__block修飾的變量會變成對象
舉個??,當我們執(zhí)行這句代碼"__block int num"之后 ,num不再是個int型,而是變成個結構體,包含如下
1. void* isa
2. int num
3. __forwarding指針
"_forwarding指針"
存在的意義
不論在任何內存位置,我們都可以通過_forwarding指針順利的訪問同一個__block變量
若沒有對__block進行copy,那么操作的是棧上的__block變量
如果copy后,不論是在棧還是堆,我們對__block的修改活賦值,都是對堆上的__block進行的
棧上的__block變量的__forwarding指針,是指向__block自身
當block外部的num改變時,__forwarding指針會去block結構體中找到里面的num對象進行賦值,但要注意這是棧上的block才會這樣
如下圖

如果對__block變量進行copy操作后,會在堆上面產(chǎn)生完全一樣的block變量
棧上的__forwarding指針指向堆上的__block變量,而堆上的__forwarding指針指向自身的__block
所以說,在經(jīng)過了copy之后,只要對這個值進行了修改,__forwarding指針改的都是堆上的值
如下圖

Block的內存管理
"block有哪幾類"
impl.isa = NSConcteteStackBlock, isa會標記block是哪種類型
全局block = NSConcreteGlobalBlock 存放在內存的已初始化數(shù)據(jù)區(qū)域中
棧block = NSConcreteStackBlock 存放在內存的棧上面
堆上面的block = NSConcreteMallocBlock 存放在內存的堆上面
"對于block的copy操作"
全局block - copy后什么也不做
棧block - copy后會在堆上產(chǎn)生一個block
堆block - copy后會增加其引用計數(shù)
問題??:
P類中有個assign修飾的block,假如在方法A中,我們P.block = ^(int){***}
因為方法A是在棧上,執(zhí)行完在內存中就銷毀了,假如在后面我們又調用了P.block
就會崩潰!!!
"block的銷毀"
棧block變量在作用域結束后就銷毀了
當我們對棧上block進行copy之后,會在堆上產(chǎn)生一模一樣的blcok,但分占了棧和堆兩塊內存空間,當作用域結束后,棧上的block會銷毀,但堆上的不銷毀
在MRC環(huán)境下,對棧上block進行copy后,會內存泄露,因為如果堆上的block沒有其他變量指向,就會產(chǎn)生內存泄露


Block的循環(huán)引用
"為什么會產(chǎn)生循環(huán)引用"
在截獲變量中,若對象P強引用block,block內部又截獲了strong類型的數(shù)組對象,就會循環(huán)引用

"怎么打破循環(huán)引用"
__weak

__block修飾符引起的循環(huán)引用


注意MCBlock不是個block,只是個類名字
上圖的問題是,在MRC下不會產(chǎn)生循環(huán)引用,但在ARC下會產(chǎn)生循環(huán)引用,引起內存泄露
解決方式
下圖這個解決方案有個弊端,也就是如果我們很長時間都沒有調用這個block的話,這個循環(huán)引用就一直存在
