OC總結篇 - Block

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)引用就一直存在

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 參考 Block編譯代碼解讀:block沒那么難(一、二、三)iOS進階——iOS(Objective-C) 內存...
    啊哈呵閱讀 884評論 0 3
  • 一、Block的底層結構及本質 (1)block本質: 從代碼可以看出,Block的本質就是NSObject. 也...
    王的for閱讀 723評論 0 2
  • 1.block介紹 概念:block是將函數(shù)及其執(zhí)行上下文封裝起來的對象。下面將通過原碼解析的方式理解概念Obje...
    細雨菲菲v閱讀 469評論 0 0
  • BLOCK block的描述: 他是類似函數(shù)指針的一個代碼塊的內聯(lián)封裝, 他可以將一個函數(shù)體作為對象傳遞 bloc...
    Chris腦閱讀 344評論 0 0
  • block的本質 block本質上也是一個OC對象,它內部也有個isa指針block是封裝了函數(shù)調用以及函數(shù)調用環(huán)...
    斑駁的流年無法釋懷閱讀 349評論 0 2

友情鏈接更多精彩內容