什么是 Blocks ?
Blocks是 C 語言的擴充功能佑菩《苣可以用一句話來表示 Blocks 的擴充功能:帶有自動變量(局部變量)的匿名函數(shù)。
- 匿名函數(shù) : 不帶有名字的函數(shù)
- 自動變量 : 局部變量(可以傳遞值的變量)殿漠,表現(xiàn)為 “截取自動變量值”
Blocks 模式 赴精?
^ 返回值類型 參數(shù)列表 表達式
注意它沒有函數(shù)名,因為它是匿名函數(shù)绞幌;返回值類型帶有^
蕾哟,因為這作為一個插入記號,便于在大量使用 Block 的時候查找。
一般來說谭确,我們的比較常用的情況是這樣的帘营。
typedef void (^TestBlock)(NSString *sendValue);
@property (nonatomic, copy) TestBlock testBlock;
if (self.testBlock) {
self.testBlock (@"TestValue");
}
__weak typeof(self) weakSelf = self;
testObject.testBlock = ^(NSString *sendValue){
__strong typeof(weakSelf) strongSelf = weakSelf;
strongSelf.testValue = sendValue;
};
問題的引出
1、block表達式到底是怎樣的?
此處逐哈,一下子會很奇怪芬迄,為什么表達式不是按 Block 語法說的那樣的?
// 沒有參數(shù)沒有返回值
TestBlock testBlock = ^void(){
NSLog(@"test");
}昂秃;
//有參數(shù)沒有返回值
TestBlock testBlock = ^(NSString *string) {
NSLog(string);
}
//有參數(shù)有返回值
TestBlock testBlock = ^NSString *(NSString *str) {
NSLog(str);
return @"testString";
}
常用的那塊舉例禀梳,是因為我們?yōu)榱烁玫氖褂盟际菍⒈磉_式和定義函數(shù)分開肠骆,便于理解和使用算途,也就可以引出下面一個問題啦。
2蚀腿、為什么用 typedef?
在函數(shù)參數(shù)和返回值中使用 block 類型變量時郊艘,記述方式很復(fù)雜,這時我們可以像使用函數(shù)指針類型時那樣唯咬,使用 typedef 來解決問題。
typedef int (^blk_t)(int);
如上所示畏浆,這樣通過使用 typeded 可聲明 “blk_t”類型變量胆胰。換一種方式思考,之前舉的例子不那樣寫應(yīng)該怎樣寫呢刻获?
@property (nonatomic, copy) void (^TestTempBlock)(NSString *sendValue);
看起上述真不是我們熟悉的感覺吧蜀涨,怪怪的,所以簡單的說通過 typedef蝎毡,函數(shù)定義變的更容易理解多啦厚柳。
3、為什么用 copy?
簡單解釋就是 Block 的生命周期是和棧是綁定在一起的沐兵,為了不被提前釋放掉别垮,需要 copy 之后讓 block 在堆上≡眩或者說是為了讓Block在初始化作用域外可以進行正常訪問外部變量碳想。
此時我們先來需要注意的是 Block 是Objective-C 對象,關(guān)于 Blcok 結(jié)構(gòu)體這塊可以去看看 唐巧的談Objective-C block的實現(xiàn)加深理解毁靶,將 block 當做對象來看時胧奔,Blcok的類就有啦,它分為三種:
- _NSConcreteStackBlock
- _NSConcreteGlobalBlock
- _NSConcreteMallocBlock
- _NSConcreteStackBlock :引用了外部變量的 block预吆,或者說使用了截獲的自動變量龙填,對應(yīng) 數(shù)據(jù)區(qū)域。
- _NSConcreteGlobalBlock:沒有引用外部變量的 block ,或者說不使用截獲的自動變量的block,對應(yīng)棧岩遗。
- _NSConcreteMallocBlock: 當block被copy時扇商,將生成的 block,也就是對應(yīng)堆。
所以此處使用 copy 的原因喘先,也就出現(xiàn)啦钳吟,為了讓 block 上__block 變量在作用域結(jié)束時不被影響而使用的欢揖。
放心的是惰帽,__block
變量中有結(jié)構(gòu)體中的成員變量(__forwarding
)可以保證無論是在棧上還是堆上都可以正確的訪問__block
變量可款。 (此處要看源碼)
但是 Block 可以用 strong 修飾嗎晴埂?在ARC下笆搓,strong和copy都可以用來修飾block籽懦,但是建議修飾block屬性使用copy崩哩,可以讓我們很容易想到它是在堆上的档悠。
MRC下則不行思喊。這是因為在MRC時期壁酬,作為屬性的block在初始化時是被存放在靜態(tài)區(qū)的,這樣在使用時如果block內(nèi)有調(diào)用外部變量恨课,那么block無法保留其內(nèi)存舆乔,在初始化的作用域內(nèi)使用并不會有什么影響,但一但出了block的初始化作用域剂公,就會引起崩潰希俩,使用copy可以將block的內(nèi)存推入堆中,這樣讓其擁有保存調(diào)用的外部變量的內(nèi)存的能力纲辽。在MRC下正常情況如果Block是使用retain修飾并且在塊內(nèi)訪問了外部變量颜武,block在出了它的初始化的作用域時并再被調(diào)用時,程序就會崩潰拖吼。
4鳞上、為什么用 __weak?
簡單直接說,就是為了防止循環(huán)引用的啊
同時注意下吊档,循環(huán)引用是怎樣產(chǎn)生的篙议?例如在某個 VC 中使用一個 Block,假如block對象賦值給了VC的屬性籍铁,那么VC就會對block有一個強引用涡上,而block中又用到了self(VC),block會對使用到的外部變量進行捕獲拒名,所以吩愧,block對VC也有一個強引用,最終造成循環(huán)引用增显,誰也無法釋放雁佳,然后就有問題啦脐帝。
self.testBlock = ^(NSString *sendValue) {
self.testValue = sendValue;
};
所以為了破壞這個環(huán),使用 __weak
是必須的糖权,讓 block 對 self 就成了弱引用堵腹,而打破了環(huán),達到我們的目的星澳。
PS: 用 __strong
是因為上述會有一個隱患疚顷,我們不知道 self 什么時候會被釋放,為了保證在block內(nèi)不會被釋放禁偎,我們添加__strong
腿堤。
想要更加深入 block,那就還得看源碼啦如暖,通過 clang笆檀,研究 block 具體的源碼實現(xiàn)方式。不過我們平常用的話盒至,了解了一些基本的也就 OK 啦酗洒,書中還有一些具體的實現(xiàn)方式,就沒有一一記錄啦枷遂。