前言
? ? ? ? iOS開發(fā)的同學(xué)們肯定都用過block,對block 的運(yùn)用熟練在我們開發(fā)過程中很有必要创夜;在此我也對其進(jìn)行一番解讀查吊,希望能在大家開發(fā)或者面試過程中有所幫助。
一叉瘩、block 概要
1、什么是block
一句話:帶有自動變量(局部變量)的匿名函數(shù)粘捎。?
Q1 什么 是匿名函數(shù) ?度娘解釋曰?;
Q2 自動變量相關(guān):C語言函數(shù)中可能用到的變量由自動變量薇缅、函數(shù)參數(shù)、靜態(tài)變量攒磨、靜態(tài)全局變量泳桦、全局變量;有興趣的還可以去探究其內(nèi)存中的存儲域
二咧纠、block用法定義
1蓬痒、block語法及變量
block 語法: ^ 返回值類型 參數(shù)列表 表達(dá)式?
^ (NSString *) (NSString *bookId){}
省略返回值寫法:
^(void){};
省略返回值及參數(shù)寫法:
^{};
2、block 實(shí)質(zhì)
簡單些一行block
聲明一個block并打印
?void(^block)(void)=^{
? ? ? ? printf("block block \n");
? ? };
? ? block();
通過在終端命令行 clang -rewrite-objc 源代碼文件名 漆羔,會生成相應(yīng)的.cpp文件梧奢,由C++編寫的源文件。具體實(shí)現(xiàn)大家可以自己去操作一下我這里貼出一段類似代碼:
C++ 源碼文件中很多內(nèi)容演痒,只要看到截圖的調(diào)用部分亲轨。
block實(shí)質(zhì)也是一個oc對象,簡單來說和class代碼的設(shè)計思想是為其構(gòu)建一個結(jié)構(gòu)體struct鸟顺,把結(jié)構(gòu)體相應(yīng)參數(shù)做索引惦蚊,遵循一定的規(guī)則,去找到相應(yīng)的實(shí)現(xiàn)方法讯嫂;同樣的我們在設(shè)計OC 對象時也應(yīng)借助這個思想蹦锋。無論對象怎么變化,他有一個基本的結(jié)構(gòu)體單元欧芽。
三莉掂、block截獲自動變量
1、block底層如何截獲自動變量值
上文中講到block實(shí)際是一個oc 對象千扔,它就有isa指針憎妙、變量等對象屬性库正,當(dāng)執(zhí)行block語句中有使用外部變量時,會將相應(yīng)的變量值 自動聲明并存儲到block 的結(jié)構(gòu)體當(dāng)中去
2厘唾、blcok 捕獲自動變量
__block 修飾詞修飾變量褥符,類似于static、auto 等c語言聲明詞抚垃,一經(jīng)修飾喷楣,底層會做相應(yīng)的處理;至于做了什么讯柔,要跟blcok 存儲域有關(guān)抡蛙。blcok類型 有三種?
棧上 NSConcreteStackBlock
堆上 NSConcreteMallocBlock
全局 NSConcreteGlobalBlock (存儲在程序的數(shù)據(jù)區(qū))
當(dāng)使用的是棧上的block 時护昧,你使用的又是局部變量魂迄,想截獲變量改變其值,必須用__block 修飾惋耙,是將該變量變?yōu)橐粋€結(jié)構(gòu)體自動變量才能被修改捣炬,示例如下:
block_t=blk;
{
__block NSMutableArray *array=[[NSMutableArray alloc] init];
blk=^(NSObject obj){
? ? [array add object:obj];
}
blk([[NSObject alloc] init]);
}
或者這么寫:
block_t=blk;
{
?NSMutableArray *array=[[NSMutableArray alloc] init];
blk=[^(NSObject obj){
[array add object:obj];
} copy]
blk([[NSObject alloc] init]);
}
這兩種寫法 都是 將變量array 從 棧copy 到 堆上,第一種是array 變量通過 block 修飾符生成一個forwarding指針绽榛,指向其拷貝到堆上的生成的自動變量結(jié)構(gòu)體湿酸;第二種是把block 拷貝到堆上截獲的變量也會被拷貝到堆上,這樣當(dāng)運(yùn)行完大括號的代碼時灭美,array 也不會被釋放推溃,知道block 運(yùn)行完成被釋放時,array 才會跟著被釋放釋放是由系統(tǒng)調(diào)用dispose完成届腐。
關(guān)于對象類型自動變量捕獲铁坎,使用方法一還是方法二有個小總結(jié):
1)block作為函數(shù)返回值返回時
2)將block賦值給類的附有__strong 修飾符的id 類型或block類型成員變量時
3)向方法名中含有usingBlock 的cocoa框架方法或GCD 的api 傳遞block 時;
除了以上幾種情況外犁苏,其他都建議使用方法二硬萍。
由于以上變量都是 strong 類型變量,如果是weak 類型變量呢围详?類似如下:
block_t=blk;
{
__block NSMutableArray __weak *array=[[NSMutableArray alloc] init];
blk=^(NSObject obj){
? ? [array add object:obj];
}
blk([[NSObject alloc] init]);
}
經(jīng)驗(yàn)證朴乖,運(yùn)行完代碼,在作用域外array 會被釋放助赞,array 不會有任何改變买羞。
3、blcok循環(huán)引用
block 的循環(huán)引用是我們平時寫代碼經(jīng)常要注意的問題雹食,循環(huán)引用的原因即為 block 強(qiáng)持有該 自動變量畜普,自動變量又強(qiáng)持有 blcok,運(yùn)行完后釋放時你等待我釋放婉徘,我等待你釋放漠嵌,由此造成死循環(huán)咐汞;類似的有多線程中的死鎖問題,二等待一運(yùn)行完一等待二運(yùn)行完導(dǎo)致兩者都在等待執(zhí)行卡死在那兒儒鹿。如下圖
避免循環(huán)引用的方式主要是兩種:
1)使用__weak 或 __unsafe_unretained修飾自動變量使得blcok 弱持有 自動變量化撕,當(dāng)block 執(zhí)行完后,blcok 和對象都能被釋放
2)使用__block 修飾自動變量 约炎, 但是 必須要調(diào)用 一次 block 植阴,才能使得 block變量對 自動變量的持有釋放,不然還是會有循環(huán)引用
4圾浅、blcok 拷貝/釋放
有一個小點(diǎn)掠手,顯示調(diào)用copy 和release 的情況,是在非ARC 情況下即需要我們主動去調(diào)用release 方法 將copy 到堆里的block 釋放掉狸捕,這里我不做詳細(xì)描述喷鸽,有興趣的可以自己去度娘問問;
block 的使用博大精深灸拍,需要更進(jìn)一步的探尋它的使用場景做祝,期待與你一起深入學(xué)習(xí)研究。鸡岗。混槐。
參考:
1、<<Object-C 高級編程iOS 與OSX 多線程和內(nèi)存管理>>