1 Blocks 概要
1.1 什么是 Blocks
Blocks是 C 語言的擴(kuò)充功能鹰服,Blocks 是帶有自動變量(局部變量)的匿名函數(shù)洞拨。
帶有自動變量值的匿名函數(shù)? 分為 “ 匿名函數(shù)” 和 “帶有自動變量值”
什么是 “帶有自動變量值”:?
2 Blocks 模式
2.1 Block 語法
與 C 語言函數(shù)定義相比佩憾,有兩點不同 :1:沒有函數(shù)名潜沦。 2:帶有 ^
第一種:
^ int (int count) { return count + 1;};
第二種:?
省略返回值類型時:Block 語法將按照 return 語句的類型盏阶,返回返回值绑谣。
第三種:?
2.2 Block 類型變量
“Block” 既指源代碼中的 Block 語法负甸,也指由 Block 語法所生成的值兄裂。
Block變量聲明:
int (^ blk) (int);
Block 類型變量的用途:
(1)自動變量(局部變量)
(2)函數(shù)參數(shù)
(3)靜態(tài)變量
(4)靜態(tài)全局變量
(5)全局變量
使用 Block 語法將 Block 賦值為 Block 類型變量:
int (^blk) (int)? = ^(int count) {return count +1 };
在函數(shù)參數(shù)中使用 Block 類型變量向函數(shù)傳遞 Block:
一種:
void func (int (^blk) (int)) {…}
另一種:
typedef? int (^blk_t) (int);
void func (blk_t blk) {…}
在函數(shù)返回值中指定 Block 類型枣察,可以將 Block 作為函數(shù)的返回值返回:
一種:
- (int (^) (int))func? ? ? ? ? ? ?//注:函數(shù)返回值為 Block時,返回類型沒有 block 變量名
{
????????return ^(int count) {return count + 1;};
}
另一種:
typedef? int (^blk_t) (int);
- (blk_t) func
{
????????????????return ^(int count) {return count + 1;};
}
用 typedef 給 block 重命名?
?用 Block 類型變量調(diào)用 Block砰盐,與通常的 C 語言變量一樣使用:
- (int) methodUsingBlock: (blk_t) blk? rate:(int) rate
{
????????return blk(rate);
}
2.3 截獲自動變量值
“帶有自動變量值的匿名函數(shù)” 中 “帶有自動變量值”在 Block 中表現(xiàn)為 “截獲自動亦是值”闷袒。?
Blocks 中,Block 表達(dá)式截獲所使用的自動變量的值岩梳,即保存該自動變量的瞬間值囊骤。因為 Block 表達(dá)式保存了自動變量的值,所以在執(zhí)行 Block 語法后蒋腮,即使改寫 Block 中使用的自動變量的值也不會影響 Block 執(zhí)行時自動變量的值。
2.4 __block 說明符
自動變量值截獲只能保存執(zhí)行 Block 語法瞬間的值藕各。保存后就不能改寫該值池摧。嘗試改寫截獲的自動變量值:會生產(chǎn)編譯錯誤?
若想在 Block 語法的表達(dá)式中將值賦給在 Block 語法外聲明的自動變量,需要在該自動變量上附加 __block 說明符激况。
使用附有 __block 說明符的自動變量可在 Block 中賦值作彤,該變量稱為 __block 變量。
2.5 截獲的自動變量
截獲?OC?對象乌逐,調(diào)用變更該對象的方法:
結(jié)論:賦值給截獲的自動變量 arrayM 的操作會產(chǎn)生編譯錯誤竭讳,但使用截獲的值卻不會有問題。
Blokc 截獲 C 語言數(shù)組:?
結(jié)論:在 Blocks 中浙踢,截獲自動變量的方法并沒有實現(xiàn)對 C 語言數(shù)組的截獲绢慢。這時,使用指針可以解決該問題洛波。?
3 Blocks 的實現(xiàn)
3.1 Block 的實質(zhì)
Block的實質(zhì)即為 Objective-C 的對象(結(jié)構(gòu)體)胰舆。
結(jié)構(gòu)體包括:
????isa類結(jié)構(gòu)指針 (三大類型:_NSConcrete[Stack | Malloc | Global ]Block)
????FuncPtr? 函數(shù)指針
????flags , reserved和 Block 截獲的自動變量
clang -rewrite-objc源代碼文件名
3.2 截獲自動變量值 (只對 Block 中使用的自動變量)
截獲自動變量值:在執(zhí)行 Block 語法時骚露,Block 語法表達(dá)式所使用的自動變量值被保存到 Block 的結(jié)構(gòu)體實例中。
3.3 __block 說明符
在 Block 中修改自動變量的兩種方法:
第一種:用 靜態(tài)局部變量缚窿、靜態(tài)全局變量棘幸、全局變量。
注:靜態(tài)局部變量:Block 結(jié)構(gòu)體中存放靜態(tài)局部變量的指針倦零。
第二種:用 __block 說明符 (__block 存儲域類型說明符) 類似于 static 误续、auto 、register 說明符扫茅,用于指定變量值的存儲到哪個存儲域中
__block變量clang后轉(zhuǎn)換為結(jié)構(gòu)體蹋嵌, Block 的結(jié)構(gòu)體實例持有指向 __block 變量的結(jié)構(gòu)體實例的指針。
2.3.4 Block 存儲域
Block 轉(zhuǎn)換為 Block 的結(jié)構(gòu)體類型的自動變量诞帐,__block 變量轉(zhuǎn)換為 __block 變量的結(jié)構(gòu)體類型的自動變量欣尼。所謂結(jié)構(gòu)體類型的自動變量,即棧上生成的該結(jié)構(gòu)體的實例停蕉。
Block 的類:
_NSConcreteGlobalBlock -存儲域:程序的數(shù)據(jù)區(qū)域(.data 區(qū))
(1)記述全局變量的地方使用 Block 語法時
(2)Block 語法的表達(dá)式中不使用截獲的自動變量時
_NSConcreteStackBlock -存儲域:棧 愕鼓;復(fù)制效果:到堆
除 Global 之外的 Block 生成的 Block 都是棧Block
_NSConcreteMallocBlock -存儲域:堆;復(fù)制效果:引用計數(shù)增加
Blocks提供了將 Block 和 _block 變量從棧上復(fù)制到堆上的方法慧起。
這樣即使 Block 語法記述的變量作用域結(jié)束菇晃,堆上的 Block 還可以繼續(xù)存在。
實際上當(dāng) ARC 有效時蚓挤,大多數(shù)情形下編譯器會恰當(dāng)?shù)剡M(jìn)行判斷磺送,自動生成將 Block 從棧上復(fù)制到堆上的代碼。
什么時候棧上的 Block 會被復(fù)制到堆上:
(1)調(diào)用 Block 的 copy 實例方法時
(2)Block 作為函數(shù)的返回值時
(2)將 Block 賦值給附有 __strong 修飾符 id 類型的類或Block 類型成員變量時
(3)方法名中含有 usingBlock 的 Cocoa 框架的方法 和 GCD 的 API
需要手動復(fù)制的情況:
(1)NSArray 類的initWithObjects
(2)向方法或函數(shù)的參數(shù)中傳遞 Block 時(可以不用手動復(fù)制)?
不管 Block 配置在何處灿意,用 copy 方法復(fù)制都不會引起任何問題估灿。在不確定時調(diào)用 copy 方法即可。
3.5 __block 變量存儲域
Block中使用 __block 變量缤剧,當(dāng) Block 從棧復(fù)制到堆時馅袁,使用的所有 __block 變量也全部被從棧復(fù)制到堆。
在多個 Block 中使用 __block 變量???
Block 超出變量作用域可存在的原因:將 Block 和 __block 變量從棧上復(fù)制到堆上
__block 變量用結(jié)構(gòu)體成員變量 __forwarding 存在的原因:實現(xiàn)無論 __block變量配置在棧上還是堆上都能正確地進(jìn)行訪問?
3.6 截獲對象
__main_block_copy_0 函數(shù)使用 _Block_object_assign 函數(shù)將對象類型對象賦值給 Block 結(jié)構(gòu)體的成員變量 array 中并持有該對象荒辕。
_Block_object_assign 函數(shù)調(diào)用相當(dāng)于 retain 實例方法的函數(shù)汗销,將對象賦值在對象類型的結(jié)構(gòu)體成員變量中。
__main_block_dispose_0 函數(shù)使用 _Block_object_dispose 函數(shù)抵窒,釋放賦值在 Block 用結(jié)構(gòu)體成員變量 array 中的對象弛针。
_Block_object_dispose 函數(shù)相當(dāng)于 release 實例方法的函數(shù),釋放賦值在對象類型的結(jié)構(gòu)體成員變量中的對象李皇。
__main_block_copy_0 和 __main_block_dispose_0 函數(shù)在 Block 從棧復(fù)制到堆時以及堆上的 Block 被廢棄時調(diào)用
3.7 __block 變量和對象
__block 說明符可指定任何類型的自動變量削茁。
3.8 Block 循環(huán)引用
在 Block 中使用附有 __strong 修飾符的對象類型自動變量,那么當(dāng) Block 從棧復(fù)制到堆時,該對象為 Block 所持有付材。這樣容易引起循環(huán)引用朦拖。
解決方法:
1. ARC :通過 __weak 或 __unsafe_unretained 修飾符(iOS4) 來替代__strong 類型的被截獲的自動變量。
2. MRC:通過 __block 說明符指定變量不被 Block 所 retain ; ARC下 __block說明符的作用僅限于使其能在 Block 中被賦值厌衔。
原理:
如果對 Block 做一次 copy 操作璧帝,Block 的內(nèi)存就會在堆中
它會將所引用的對象做一次 retain 操作
非 ARC : 如果所引用的對象用了 __block 修飾,就不會做 retain 操作
ARC :如果所引用的對象用了 __unsafe_unretained 或 __weak 修飾富寿,就不會做 retain 操作睬隶。
3.9 copy/release
ARC無效時,一般需要手動將 Block 從棧復(fù)制到堆页徐。也要釋放復(fù)制的 Block苏潜。
用 copy 方法復(fù)制 ,release 方法釋放变勇。
注:靜態(tài)局部變量恤左,靜態(tài)全局變量,全局變量 的相同與不同點:
相同點:存儲區(qū)都在全局區(qū)
不同點: 作用域不同
全局變量: 其它文件需要用extern關(guān)鍵字再次聲明這個全局變量搀绣。
? ? 靜態(tài)全局變量:僅所在的文件才可以訪問
? ? 靜態(tài)局部變量:僅對其所在的函數(shù)體作用域可見飞袋。
截獲?OC?對象,調(diào)用變更該對象的方法: