深入淺出Block


ps:看網上的東西最好自己試一下贝奇,別人講的東西不一定是正確的陕习。

Block概要

什么是Block

Blocks是C語言的擴充功能。可以用一句話來表示Blocks的擴充功能:帶有自動變量(局部變量)的匿名函數。命名就是工作的本質胳嘲,函數名汽纤、變量名呆瞻、方法名、屬性名、類名和框架名都必須具備归粉。而能夠編寫不帶名稱的函數對程序員來說相當有吸引力。例如:我們要進行一個URL的請求滴劲。那么請求結果以何種方式通知調用者呢?通常是經過代理(delegate)但是,寫delegate本身就是成本,我們需要寫類倦青、方法等等峭沦。這時候取募,我們就用到了block旺聚。block提供了類似由C++和OC類生成實例或對象來保持變量值的方法。像這樣使用block可以不聲明C++和OC類谐丢,也沒有使用靜態(tài)變量、靜態(tài)全局變量或全局變量衰腌,僅用編寫C語言函數的源碼量即可使用帶有自動變量值的匿名函數饶囚。其他語言中也有block概念歇万。

Block的實現(xiàn)

下面我們來定義一個簡單的block,block的語法看上去好像很特別壕曼,但實際上是作為極為普通的C語言代碼來處理的大渤。這里我們借住clang編譯器的能力:具有轉化為我們可讀源代碼的能力较曼『嵫眩控制臺命令是: clang -rewrite-objc 源代碼文件名仅炊。


經過 clang -rewrite-objc 之后呆馁,代碼編程這樣了(簡化后代碼,讀者可以搜索關鍵字在生成文件中查找):

看上去有點暈對不對誓沸,沒關系我們一個個來分析。

__block_impl:更像一個block的基類炭菌,所有block都具備這些字段。

__main_block_impl_0:block變量旭蠕。

__main_block_func_0:雖然block叫匿名函數停团。但是這個函數還是被編譯器起了個名字。(圖片里面 的顏色貌似不對勁)掏熬。

__main_block_desc_0:block的描述佑稠,注意,他有一個實例__main_block_desc_0_DATA孽江,上述命名是有規(guī)則的:main是block所在函數的名字讶坯,后綴0則是這個函數中的第0個block。由于上面是C++的代碼岗屏,可以將__main_block_impl_0的結構體總結一下辆琅,得到如下形式:

因此我可以看出來所謂的block就是一個object-c 對象漱办。為什么這么說呢,在runtime機制的時候會講到相關內容婉烟,到時候你就明白了娩井。

截獲自動變量值

我們看下面一段代碼,你猜猜是什么結果呢似袁?

上面這段代碼的值是10洞辣,你可能會感到很疑惑。block截獲自動變量的瞬時值昙衅。因為block保存了自動變量的值扬霜,所以在執(zhí)行block語法后,即使改寫block中使用的自動變量的值也不會影響block執(zhí)行時自動變量的值而涉。

如果你強行在block里面修改val的值著瓶,那么編譯器將會報錯,我們可以這么理解啼县,block捕獲的自動變量會默認轉化為const類型材原,不可修改了,如果我們要改的話只需要在定義變量的時候給它加__block修飾符就可以了季眷。但是如果我們捕獲的是oc對象呢余蟹?

就比如一個NSMutableArray *array。^{[array addObject:obj];};這么寫是沒有問題的子刮,因為array只是一個指針而已威酒,我們并沒有改變指針的值。

還有一種情況我們也可以很好的解釋:const char text[] = "hello";? ^{ printf("%c\n",text[2]);}; 這樣會編譯錯誤挺峡。為何兼搏?這是因為捕獲自動變量的方法并沒有實現(xiàn)C語言數組類型。

可以通過指針代替:const char *text= "hello";那么這個block的對象結構是什么樣呢沙郭,請看下面:

這個val是如何傳遞到block結構體中的呢?

就像C里面初始化結構體一樣的裳朋,注意函數調用最后一個參數病线,即val參數。那么函數調用的代碼頁轉化為下面這樣了.這里的cself跟C++的this和OC的self一樣鲤嫡。

所以送挑,block捕獲變量更像是:函數按值傳遞。是不是很簡單呢暖眼?

__block說明符

前面講過block所在函數中的惕耕,捕獲自動變量。但是不能修改它诫肠,不然就是編譯錯誤司澎。但是可以改變全局變量欺缘、靜態(tài)變量、全局靜態(tài)變量挤安。其實這兩個特點不難理解:

第一谚殊、為何不讓修改變量:這個是編譯器決定的。理論上當然可以修改變量了蛤铜,只不過block捕獲的是自動變量的副本嫩絮,名字一樣。為了不給開發(fā)者迷惑围肥,干脆不讓賦值剿干。道理有點像:函數參數,要用指針穆刻,不然傳遞的是副本置尔。

第二、可以修改靜態(tài)變量的值蛹批。靜態(tài)變量屬于類的撰洗,不是某一個變量。所以block內部不用調用cself指針腐芍。所以block可以調用差导。

解決block不能保存值這一問題的另外一個辦法是使用__block修飾符。

該源碼轉化后如下:

比__main_block_impl_0中自然多了__block_byreg_val_0的一個字段猪勇。注意:__block_byref_val_0結構體中有自身的指針對象设褐,難道要_block int val = 10;這一行代碼,轉化成了下面的結構體__block)byref_val_0 val = {0,&val,0,sizeof(__block_byref_val_0),10};//自己持有自己的指針泣刹。

它竟然變成了結構體了助析。之所以為啥要生成一個結構體,后面在詳細講講椅您。反正不能直接保存val的指針外冀,因為val是棧上的,保存棧變量的指針很危險掀泳。

block存儲區(qū)域


這就需要引入三個名詞:

● _NSConcretStackBlock

● _NSConcretGlobalBlock

● _NSConcretMallocBlock


正如它們名字說的那樣雪隧,說明了block的三種存儲方式:棧、全局员舵、堆脑沿。__main_block_impl_0結構體中的isa就是這個值。

定義在函數外面的block是global的马僻;另外如果函數內部的block庄拇,但是沒有捕獲任何自動變量,那么它也是全局的韭邓。比如下面這樣的代碼:

雖然措近,這個block在循環(huán)內溶弟,但是blk的地址總是不變的。說明這個block在全局段熄诡。

一種情況在非ARC下是無法編譯的:

typedef int(^blk_t)(int);

blk_t func(int rate){

return ^(int count){return rate*count;}

}

這是因為:block捕獲了棧上的rate自動變量可很,此時rate已經變成了一個結構體,而block中擁有這個結構體的指針凰浮。即如果返回block的話就是返回局部變量的指針我抠。而這一點恰是編譯器已經斷定了。在ARC下沒有這個問題袜茧,是因為ARC使用了autorelease了菜拓。

有時候我們需要調用block 的copy函數,將block拷貝到堆上笛厦∧啥Γ看下面的代碼:

我們經過調試發(fā)現(xiàn)如下結果:

它的第一個對象是在堆上面,第二個個在棧上面裳凸,我表示很困惑贱鄙。

這段代碼在最后一行blk()會異常,因為數組中的block是棧上姨谷。因為val是棧上的逗宁。解決辦法就是調用copy方法,將它復制到堆上面。

不管block配置在何處梦湘,用copy方法復制都不會引起任何問題瞎颗。在ARC環(huán)境下,如果不確定是否要copy block盡管copy即可捌议。ARC會打掃戰(zhàn)場哼拔。

注意:在棧上調用copy那么復制到堆上,在全局block調用copy什么也不做瓣颅,在堆上調用block 引用計數增加

__block變量存儲區(qū)域

當block被復制到堆上時倦逐,他所捕獲的對象、變量也全部復制到堆上宫补。

回憶一下block捕獲自動變量的時候僻孝,自動變量將編程一個結構體,結構體中有一個字段叫__forwarding守谓,用于指向自動這個結構體。那么有了這個__forwarding指針您单,無論是棧上的block還是被拷貝到堆上斋荞,那么都會正確的訪問自動變量的值。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末虐秦,一起剝皮案震驚了整個濱河市平酿,隨后出現(xiàn)的幾起案子凤优,更是在濱河造成了極大的恐慌,老刑警劉巖蜈彼,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件筑辨,死亡現(xiàn)場離奇詭異,居然都是意外死亡幸逆,警方通過查閱死者的電腦和手機棍辕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來还绘,“玉大人楚昭,你說我怎么就攤上這事∨那辏” “怎么了抚太?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長昔案。 經常有香客問我尿贫,道長,這世上最難降的妖魔是什么踏揣? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任庆亡,我火速辦了婚禮,結果婚禮上呼伸,老公的妹妹穿的比我還像新娘身冀。我一直安慰自己,他們只是感情好括享,可當我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布搂根。 她就那樣靜靜地躺著,像睡著了一般铃辖。 火紅的嫁衣襯著肌膚如雪剩愧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天仁卷,我揣著相機與錄音,去河邊找鬼锦积。 笑死,一個胖子當著我的面吹牛歉嗓,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼带膀,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了橙垢?” 一聲冷哼從身側響起垛叨,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎柜某,沒想到半個月后嗽元,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡莺琳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年还棱,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惭等。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡珍手,死狀恐怖,靈堂內的尸體忽然破棺而出辞做,到底是詐尸還是另有隱情琳要,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布秤茅,位于F島的核電站稚补,受9級特大地震影響,放射性物質發(fā)生泄漏框喳。R本人自食惡果不足惜课幕,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望五垮。 院中可真熱鬧乍惊,春花似錦、人聲如沸放仗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽诞挨。三九已至莉撇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間惶傻,已是汗流浹背棍郎。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留银室,地道東北人涂佃。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓静秆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親巡李。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,652評論 2 354

推薦閱讀更多精彩內容

  • 一句話總結block : 帶有局部變量的匿名函數 閉包在其它編程語言的名稱 iOS閉包的聲明與定義 博主iOS開發(fā)...
    王韓峰閱讀 1,033評論 4 5
  • 前言 Blocks是C語言的擴充功能扶认,而Apple 在OS X Snow Leopard 和 iOS 4中引入了這...
    小人不才閱讀 3,768評論 0 23
  • 《Objective-C高級編程》這本書就講了三個東西:自動引用計數侨拦、block、GCD辐宾,偏向于從原理上對這些內容...
    WeiHing閱讀 9,810評論 10 69
  • Block基礎回顧 1.什么是Block狱从? 帶有局部變量的匿名函數(名字不重要季研,知道怎么用就行)誉察,差不多就與C語言...
    Bugfix閱讀 6,762評論 5 61
  • Blocks Blocks Blocks 是帶有局部變量的匿名函數 截取自動變量值 int main(){ ...
    南京小伙閱讀 925評論 1 3