block詳解

block本質(zhì):

每個(gè)OC對(duì)象內(nèi)部都有isa指針,block也不例外;block其實(shí)就是封裝函數(shù)調(diào)用,以及函數(shù)調(diào)用環(huán)境的OC對(duì)象。

block底層結(jié)構(gòu):

block底層結(jié)構(gòu)

block的類(lèi)型:

內(nèi)存幾大區(qū)域

常見(jiàn)的有以下三種block:

NSMallocBlock :存放在堆區(qū)的 Block

NSStackBlock??: 存放在棧區(qū)的 Block

NSGlobalBlock : 存放在全局區(qū)的 Block

當(dāng)block 沒(méi)有截獲外部變量、截獲全局變量的都是屬于全局區(qū)的 Block,即 GlobalBlock;其余的都是棧區(qū)的 Block,即 StackBlock;

對(duì)于全局區(qū)的 Block,是不存在作用域的問(wèn)題,但是棧區(qū) Block 不同,在作用域結(jié)束后就會(huì) pop 出棧,__block 變量也是在棧區(qū)的,同理作用域結(jié)束也會(huì) pop 出棧。

為了解決作用域的問(wèn)題,block 提供了 copy 函數(shù),將 block 從棧復(fù)制到堆上,在 MRC 環(huán)境下需要我們自己調(diào)用 block_copy??函數(shù),這里就是為什么 MRC 下,我們?yōu)槭裁葱枰?copy 來(lái)修飾 Block 的原因。

MRC情況下:

MRC的情況下

ARC情況下:

編譯器會(huì)根據(jù)情況自動(dòng)添加copy,將棧上的block拷貝到堆上,比如以下情況:

1、block作為函數(shù)返回值

2、block賦值給__strong指針

3、block作為Cocoa API中的方法名含有usingBlock的方法參數(shù)時(shí)

4、block作為GCD API參數(shù)時(shí)

每一種類(lèi)型的block調(diào)用copy后的結(jié)果如下:

使用copy結(jié)果示意圖

block捕獲機(jī)制:

block捕獲機(jī)制
捕獲auto、static局部變量

因?yàn)閍uto的局部變量在離開(kāi)作用域會(huì)銷(xiāo)毀,所以block內(nèi)部使用值傳遞的方式捕獲到,因此在block內(nèi)部不能更改auto變量的值,像int因?yàn)橹凳侵苯哟鎯?chǔ)在變量?jī)?nèi)存里面,所以不能更改;而static因?yàn)槭侵羔槀鬟f所有能更改。

更改mArr內(nèi)容
更改mArr指針地址

因?yàn)镹SMutableArray 變量里面其實(shí)存的是指針地址,所以直接操作指針里面的內(nèi)容是可以的,如果直接更改變量存儲(chǔ)的指針地址就不行。

如果是全局變量不會(huì)捕獲,不管在哪個(gè)函數(shù)都能直接訪問(wèn);局部變量因?yàn)樽饔糜虻膯?wèn)題,可能存在跨函數(shù)訪問(wèn)所以會(huì)捕獲。

如果block中引用實(shí)例變量(比如說(shuō)name)不管是通過(guò)self.name還是_name,block都會(huì)捕獲self對(duì)象.因?yàn)榉椒ǖ恼{(diào)用默認(rèn)傳遞兩個(gè)參數(shù)(self,sel)所以self為局部變量,block引用局部變量都會(huì)捕獲。

既然block對(duì)局部auto變量是值傳遞,那么如果想要在block內(nèi)部修改auto變量怎么修改?

如果block外部變量是auto變量可以使用__block;如果是static靜態(tài)變量 或者全局變量,則不需要修飾也能更改。(參考捕獲機(jī)制)

原理:在ARC環(huán)境下,編譯器會(huì)盡可能給我們自動(dòng)添加 copy 操作。使用copy從棧復(fù)制到堆上,__block 修飾的變量也會(huì)從棧復(fù)制到堆上;為了結(jié)構(gòu)體 __block 變量無(wú)論在棧上還是在堆上,都可以正確的訪問(wèn)變量,我們需要 forwarding 指針;forwarding指針的作用:保證當(dāng)我們將 Block 從??截惖蕉阎?,修改的變量都是同一份。

forwarding原理:在 Block 從棧復(fù)制到堆上的時(shí)候,原本棧上結(jié)構(gòu)體的 forwarding 指針,會(huì)改變指向,直接指向堆上的結(jié)構(gòu)體。這樣子就可以保證之后我們都是訪問(wèn)同一個(gè)結(jié)構(gòu)體中的變量,這里就是為什么 __block 修飾的變量,在 Block 內(nèi)部中可以修改的原因了。

forwarding指針

__block底層實(shí)現(xiàn):

當(dāng)__block變量在棧上時(shí):

并不會(huì)對(duì)指向的對(duì)象產(chǎn)生強(qiáng)引用。

當(dāng)__block變量被copy到堆上時(shí):

copy函數(shù)內(nèi)會(huì)調(diào)用_Block_object_assign函數(shù);_Block_object_assign函數(shù)會(huì)根據(jù)auto變量(對(duì)象類(lèi)型)的修飾符(__strong,__weak, __unsafe_unretained)做出相應(yīng)的操作形成強(qiáng)、弱引用;

當(dāng)__block變量被從堆上移除時(shí):

會(huì)調(diào)用Block內(nèi)部的dispose函數(shù) dispose函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_dispose函數(shù)?_Block_object_dispose函數(shù)會(huì)自動(dòng)釋放引用的auto變量,類(lèi)似于release。

block引起循環(huán)引用場(chǎng)景:

當(dāng) A 持有 B,B 又持有 A,這個(gè)時(shí)候就會(huì)出現(xiàn)循環(huán)引用。Block 對(duì)于外部變量都會(huì)追加到結(jié)構(gòu)體中,所以在實(shí)現(xiàn) Block 時(shí)候需要注意這個(gè)問(wèn)題。

解決方案:

ARC 環(huán)境一般我們用 __weak 來(lái)打破,MRC 環(huán)境的話,我們可以使用 __block 來(lái)打破循環(huán)引用。

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

相關(guān)閱讀更多精彩內(nèi)容

  • 目錄1.Block 的基本使用2.Block 的底層數(shù)據(jù)結(jié)構(gòu)3.Block 的變量捕獲機(jī)制3.1 auto 類(lèi)型的...
    Swifter1閱讀 1,116評(píng)論 0 9
  • iOS開(kāi)發(fā)---Block詳解 Block的基礎(chǔ) 什么是Blocks? 用一句話來(lái)描述:帶有自動(dòng)變量的匿名函數(shù)(是...
    祀夢(mèng)_閱讀 1,074評(píng)論 0 6
  • 第一部分:Block本質(zhì) Q:什么是Block,Block的本質(zhì)是什么? block本質(zhì)上也是一個(gè)OC對(duì)象,它內(nèi)部...
    sheldon_龍閱讀 615評(píng)論 0 0
  • 1.明白如何定義block類(lèi)型 定義Block類(lèi)型: typedef 返回值類(lèi)型 Block名字 參數(shù) block...
    哈庫(kù)吶瑪塔塔__閱讀 871評(píng)論 0 0
  • 一、Block本質(zhì) Block是“帶有自動(dòng)變量值的匿名函數(shù)”。 所謂的匿名函數(shù)就是不帶有名稱的函數(shù) 但它究竟是什么...
    楓葉情結(jié)閱讀 624評(píng)論 1 0

友情鏈接更多精彩內(nèi)容