iOS-block

1.相關概念
在這篇筆記開始之前梭冠,我們需要對以下概念有所了解红伦。
1.1 操作系統(tǒng)中的棧和堆
注:這里所說的堆和棧與數據結構中的堆和棧不是一回事窃诉。
我們先來看看一個由C/C++/OBJC編譯的程序占用內存分布的結構:

block1.jpg

棧區(qū)(stack):由系統(tǒng)自動分配惜论,一般存放函數參數值彩掐、局部變量的值等焕数。由編譯器自動創(chuàng)建與釋放泽疆。其操作方式類似于數據結構中的棧要门,即后進先出、先進后出的原則冠蒋。
例如:在函數中申明一個局部變量int b;系統(tǒng)自動在棧中為b開辟空間羽圃。
堆區(qū)(heap):一般由程序員申請并指明大小,最終也由程序員釋放抖剿。如果程序員不釋放朽寞,程序結束時可能會由OS回收胚吁。對于堆區(qū)的管理是采用鏈表式管理的,操作系統(tǒng)有一個記錄空閑內存地址的鏈表愁憔,當接收到程序分配內存的申請時,操作系統(tǒng)就會遍歷該鏈表孽拷,遍歷到一個記錄的內存地址大于申請內存的鏈表節(jié)點吨掌,并將該節(jié)點從該鏈表中刪除,然后將該節(jié)點記錄的內存地址分配給程序脓恕。

鏈表:是一種常見的基礎數據結構膜宋,一般分為單向鏈表、雙向鏈表炼幔、循環(huán)鏈表秋茫。以下為單向鏈表的結構圖:

block2.jpg

單向鏈表是鏈表中最簡單的一種,它包含兩個區(qū)域乃秀,一個信息域和一個指針域肛著。信息域保存或顯示關于節(jié)點的信息,指針域儲存下一個節(jié)點的地址跺讯。

上述的空閑內存地址鏈表的信息域保存的就是空閑內存的地址枢贿。

全局區(qū)/靜態(tài)區(qū):顧名思義,全局變量和靜態(tài)變量存儲在這個區(qū)域刀脏。只不過初始化的全局變量和靜態(tài)變量存儲在一塊局荚,未初始化的全局變量和靜態(tài)變量存儲在一塊。程序結束后由系統(tǒng)釋放愈污。

文字常量區(qū):這個區(qū)域主要存儲字符串常量耀态。程序結束后由系統(tǒng)釋放。
程序代碼區(qū):這個區(qū)域主要存放函數體的二進制代碼暂雹。

strcpy函數
原型聲明:extern char strcpy(char dest, const char *src);
功能:把從src地址開始且含有NULL結束符的字符串復制到以dest開始的地址空間首装。

1.2 閉包(Closure)
閉包就是一個函數,或者一個指向函數的指針擎析,加上這個函數執(zhí)行的非局部變量簿盅。
說的通俗一點,就是閉包允許一個函數訪問聲明該函數運行上下文中的變量揍魂,甚至可以訪問不同運行上文中的變量桨醋。

2.blcok基礎知識
block實際上就是Objective-C語言對閉包的實現(xiàn)。
2.1 block的原型及定義
我們來看看block的原型:

NSString * ( ^ myBlock )( int );

上面的代碼聲明了一個block(^)原型现斋,名字叫做myBlock喜最,包含一個int型的參數,返回值為NSString類型的指針庄蹋。

下面來看看block的定義:

myBlock = ^( int paramA ){
     return [ NSString stringWithFormat: @"Passed number: %i", paramA ];
};

上面的代碼中瞬内,將一個函數體賦值給了myBlock變量迷雪,其接收一個名為paramA的參數,返回一個NSString對象虫蝶。
注意:一定不要忘記block后面的分號章咧。
定義好block后,就可以像使用標準函數一樣使用它了:

myBlock(7);

由于block數據類型的語法會降低整個代碼的閱讀性能真,所以常使用typedef來定義block類型赁严。例如,下面的代碼創(chuàng)建了GetPersonEducationInfo和GetPersonFamilyInfo兩個新類型粉铐,這樣我們就可以在下面的方法中使用更加有語義的數據類型疼约。

// Person.h
#import // Define a new type for the block
typedef NSString * (^GetPersonEducationInfo)(NSString *);
typedef NSString * (^GetPersonFamilyInfo)(NSString *);
@interface Person : NSObject
- (NSString *)getPersonInfoWithEducation:(GetPersonEducationInfo)educationInfo
    andFamily:(GetPersonFamilyInfo)familyInfo;
@end

我們用一張大師文章里的圖來總結一下block的結構:

block5.png

2.2 將block作為參數傳遞

// .h
-(void) testBlock:( NSString * ( ^ )( int ) )myBlock;
// .m
-(void) testBlock:( NSString * ( ^ )( int ) )myBlock
{
    NSLog(@"Block returned: %@", myBlock(7) );
}

由于Objective-C是強制類型語言,所以作為函數參數的block也必須要指定返回值的類型蝙泼,以及相關參數類型程剥。

2.3 閉包性
上文說過,block實際是Objc對閉包的實現(xiàn)汤踏。
我們來看看下面代碼:

#import void logBlock( int ( ^ theBlock )( void ) )
{
    NSLog( @"Closure var X: %i", theBlock() );
}
int main( void )
{
    NSAutoreleasePool * pool;
    int ( ^ myBlock )( void );
    int x;
    pool = [ [ NSAutoreleasePool alloc ] init ];
    x = 42;
    myBlock = ^( void )
    {
        return x;
    };
    logBlock( myBlock );
    [ pool release ];
    return EXIT_SUCCESS;
}

上面的代碼在main函數中聲明了一個整型织鲸,并賦值42,另外還聲明了一個block茎活,該block會將42返回昙沦。然后將block傳遞給logBlock函數,該函數會顯示出返回的值42载荔。即使是在函數logBlock中執(zhí)行block盾饮,而block又聲明在main函數中,但是block仍然可以訪問到x變量懒熙,并將這個值返回丘损。

注意:block同樣可以訪問全局變量,即使是static工扎。

2.4 block中變量的復制與修改
對于block外的變量引用徘钥,block默認是將其復制到其數據結構中來實現(xiàn)訪問的,如下圖:

block1.jpg

通過block進行閉包的變量是const的肢娘。也就是說不能在block中直接修改這些變量呈础。來看看當block試著增加x的值時,會發(fā)生什么:

myBlock = ^( void )
{
    x++;
    return x;
};

編譯器會報錯橱健,表明在block中變量x是只讀的而钞。

有時候確實需要在block中處理變量,怎么辦拘荡?別著急臼节,我們可以用__block關鍵字來聲明變量,這樣就可以在block中修改變量了。

基于之前的代碼网缝,給x變量添加__block關鍵字巨税,如下:

__block int x;

對于用__block修飾的外部變量引用,block是復制其引用地址來實現(xiàn)訪問的粉臊,如下圖:

bock2.jpg

3.block的類型
block有幾種不同的類型草添,每種類型都有對應的類,上述中isa指針就是指向這個類扼仲。這里列出常見的三種類型:
_NSConcreteGlobalBlock:全局的靜態(tài)block果元,不會訪問任何外部變量,不會涉及到任何拷貝犀盟,比如一個空的block。例如:

#include int main()
{
    ^{ printf("Hello, World!\n"); } ();
    return 0;
}

_NSConcreteStackBlock:保存在棧中的block蝇狼,當函數返回時被銷毀阅畴。例如:

#include int main()
{
    char a = 'A';
    ^{ printf("%c\n",a); } ();
    return 0;
}

_NSConcreteMallocBlock:保存在堆中的block,當引用計數為0時被銷毀迅耘。該類型的block都是由_NSConcreteStackBlock類型的block從棧中復制到堆中形成的贱枣。例如下面代碼中,在exampleB_addBlockToArray方法中的block還是_NSConcreteStackBlock類型的颤专,在exampleB方法中就被復制到了堆中纽哥,成為_NSConcreteMallocBlock類型的block:

void exampleB_addBlockToArray(NSMutableArray *array) {
    char b = 'B';
    [array addObject:^{
            printf("%c\n", b);
    }];
}
void exampleB() {
    NSMutableArray *array = [NSMutableArray array];
    exampleB_addBlockToArray(array);
    void (^block)() = [array objectAtIndex:0];
    block();
}

總結一下:

_NSConcreteGlobalBlock類型的block要么是空block,要么是不訪問任何外部變量的block栖秕。它既不在棧中春塌,也不在堆中,我理解為它可能在內存的全局區(qū)簇捍。

_NSConcreteStackBlock類型的block有閉包行為只壳,也就是有訪問外部變量,并且該block只且只有有一次執(zhí)行暑塑,因為棧中的空間是可重復使用的吼句,所以當棧中的block執(zhí)行一次之后就被清除出棧了,所以無法多次使用事格。

_NSConcreteMallocBlock類型的block有閉包行為惕艳,并且該block需要被多次執(zhí)行。當需要多次執(zhí)行時驹愚,就會把該block從棧中復制到堆中远搪,供以多次執(zhí)行。

4 copy()和dispose()
上文中提到么鹤,如果我們想要在以后繼續(xù)使用某個block终娃,就必須要對該block進行拷貝操作,即從棧空間復制到堆空間棠耕。所以拷貝操作就需要調用Block_copy()函數余佛,block的descriptor中有一個copy()輔助函數,該函數在Block_copy()中執(zhí)行窍荧,用于當block需要拷貝對象的時候辉巡,拷貝輔助函數會retain住已經拷貝的對象。
既然有有copy那么就應該有release蕊退,與Block_copy()對應的函數是Block_release()郊楣,它的作用不言而喻,就是釋放我們不需要再使用的block瓤荔,block的descriptor中有一個dispose()輔助函數净蚤,該函數在Block_release()中執(zhí)行,負責做和copy()輔助函數相反的操作输硝,例如釋放掉所有在block中拷貝的變量等今瀑。
5.總結
以上內容是我學習各大師的文章后對自己學習情況的一個記錄,其中有部分文字和代碼示例是來自大師的文章点把,還有一些自己的理解橘荠,如有錯誤還請大家勘誤。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末郎逃,一起剝皮案震驚了整個濱河市哥童,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌褒翰,老刑警劉巖贮懈,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異优训,居然都是意外死亡错邦,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門型宙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來撬呢,“玉大人,你說我怎么就攤上這事妆兑』昀梗” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵搁嗓,是天一觀的道長芯勘。 經常有香客問我,道長腺逛,這世上最難降的妖魔是什么荷愕? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上安疗,老公的妹妹穿的比我還像新娘抛杨。我一直安慰自己,他們只是感情好荐类,可當我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布怖现。 她就那樣靜靜地躺著,像睡著了一般玉罐。 火紅的嫁衣襯著肌膚如雪屈嗤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天吊输,我揣著相機與錄音饶号,去河邊找鬼。 笑死季蚂,一個胖子當著我的面吹牛讨韭,可吹牛的內容都是我干的。 我是一名探鬼主播癣蟋,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼狰闪!你這毒婦竟也來了疯搅?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤埋泵,失蹤者是張志新(化名)和其女友劉穎幔欧,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體丽声,經...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡礁蔗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了雁社。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浴井。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖霉撵,靈堂內的尸體忽然破棺而出磺浙,到底是詐尸還是另有隱情,我是刑警寧澤徒坡,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布撕氧,位于F島的核電站,受9級特大地震影響喇完,放射性物質發(fā)生泄漏伦泥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望不脯。 院中可真熱鬧府怯,春花似錦、人聲如沸跨新。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽域帐。三九已至赘被,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間肖揣,已是汗流浹背民假。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留龙优,地道東北人羊异。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像彤断,于是被迫代替她去往敵國和親野舶。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內容

  • 前言 Blocks是C語言的擴充功能宰衙,而Apple 在OS X Snow Leopard 和 iOS 4中引入了這...
    小人不才閱讀 3,773評論 0 23
  • 《Objective-C高級編程》這本書就講了三個東西:自動引用計數平道、block、GCD供炼,偏向于從原理上對這些內容...
    WeiHing閱讀 9,837評論 10 69
  • 《Objective-C高級編程》是一本有趣又難懂的書一屋,全書就講了引用計數、Block袋哼、GCD三個概念冀墨,有趣是因為...
    kamous閱讀 55,270評論 22 206
  • Block是iOS開發(fā)中一種比較特殊的數據結構,它可以保存一段代碼涛贯,在合適的地方再調用诽嘉,具有語法簡介、回調方便弟翘、編...
    飛魚灣閱讀 4,127評論 0 7
  • 1含懊、概述 閉包 = 一個函數「或指向函數的指針」+ 該函數執(zhí)行的外部的上下文變量「也就是自由變量」;Block 是...
    DeerRun閱讀 670評論 0 0