詳細(xì)的探討一下Block(討論篇、基礎(chǔ)篇郁副、實(shí)質(zhì)篇)

章節(jié)目錄

  • 關(guān)于Block的討論篇
  • Block的基礎(chǔ)篇
  • Block的實(shí)質(zhì)篇
  • 討論篇:

為什么要看Block减牺?
  1. 為了更熟練地使用Block
  • 為了擴(kuò)展程序編程的思維

  • 了解底層的運(yùn)作原理,有助于與他人探討相關(guān)問(wèn)題

  • 公認(rèn)的大神中基本都會(huì)存谎,作為一個(gè)菜鳥(niǎo)要多向大牛學(xué)習(xí)

  • 很多面試都需要

  • 基礎(chǔ)篇:

什么是block拔疚?

Block 一個(gè)帶有自動(dòng)變量的匿名函數(shù)。
匿名函數(shù)是因?yàn)锽lock沒(méi)有函數(shù)名稱既荚,由^ 返回值類型 入?yún)㈩愋?表達(dá)式 組成稚失。但可以賦值給Block類型變量。
自動(dòng)變量在Block中表現(xiàn)為截獲自動(dòng)變量值固以,指Block內(nèi)部調(diào)用外部變量時(shí)會(huì)捕獲該變量在此瞬間的值墩虹。

Block長(zhǎng)什么樣?

通常: ^ 返回值類型 參數(shù)類型 表達(dá)式憨琳,如: ^int(int count){return count + 1; }
但無(wú)論是整型還是無(wú)返回值诫钓,返回值類型都可省略為: ^ 參數(shù)列表 表達(dá)式
省略返回值,根據(jù)表達(dá)式中有無(wú)return決定是否有返回值篙螟,如果有則返回該返回值類型菌湃,如果沒(méi)有就是無(wú)返回值。所以遍略,所有return返回值類型都必須相同惧所。
如:^(int count){return count + 1;}
若沒(méi)有參數(shù),則參數(shù)類型也可省略绪杏,簡(jiǎn)化為:表達(dá)式下愈。如:{NSLog(@“DrunkenMouse”);}

Block類型變量

int(^blk)(int) ; blk就是Block類型變量

簡(jiǎn)化Block聲明與調(diào)用

typedef int (^blk_t)(int); 則用blk_t聲明就代表此block
如int類型a 為 int a; 則int (^blk_t)(int)類型的blk 為 blk_t blk;
而調(diào)用則就 int result = blk(10);

__block說(shuō)明符

若無(wú)__block修飾,則Block語(yǔ)法中只能捕獲博并保存調(diào)用瞬間時(shí)的外部自動(dòng)變量值蕾久,且不支持修改势似。
若想要修改外部自動(dòng)變量就需要添加__block修飾符,此時(shí)此自動(dòng)變量可稱為_(kāi)_block變量僧著。
但是對(duì)于OC對(duì)象來(lái)說(shuō)履因,調(diào)用變更該對(duì)象的方法不會(huì)發(fā)生錯(cuò)誤。
如NSMutableArray的對(duì)象array盹愚,調(diào)用其addObject方法修改其值時(shí)不會(huì)發(fā)生錯(cuò)誤栅迄。但若對(duì)array進(jìn)行賦值操作就會(huì)發(fā)生錯(cuò)誤。
也就是賦值會(huì)報(bào)錯(cuò)皆怕,但使用不會(huì)報(bào)錯(cuò)毅舆。
另外西篓,Block中無(wú)法截獲C語(yǔ)言中的數(shù)組。因此對(duì)于C語(yǔ)言數(shù)組而言憋活,無(wú)論調(diào)用污淋,還是賦值都會(huì)報(bào)錯(cuò)。

  • 實(shí)質(zhì)篇:

Block是作為C語(yǔ)言源碼來(lái)處理的余掖。通過(guò)支持Block的編譯器寸爆,將Block源碼轉(zhuǎn)換成C語(yǔ)言編譯器能夠處理的源代碼,而后作為極為普通的C語(yǔ)言源碼進(jìn)行編譯盐欺。

可手動(dòng)通過(guò)clang的-rewrite-objc轉(zhuǎn)換為C++源碼赁豆。說(shuō)是C++,其實(shí)只是使用了C++中的struct結(jié)構(gòu)的C語(yǔ)言源碼冗美。

Block是OC對(duì)象魔种,其結(jié)構(gòu)體內(nèi)部的isa指針?lè)胖迷揃lock的信息的地址,形同OC對(duì)象中的元類

一個(gè)無(wú)參無(wú)反的最簡(jiǎn)單Block

void (^blk)(void) = ^{printf(“Block\n”)};
blk();

通過(guò)clang轉(zhuǎn)換后會(huì)包括一大串代碼粉洼,而這一大串代碼等稍后總結(jié)時(shí)再看节预,現(xiàn)在先看下源碼中的各個(gè)結(jié)構(gòu)體:

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
}

此結(jié)構(gòu)體包含當(dāng)前Block的實(shí)例地址與相關(guān)信息。
首先可以看到其內(nèi)部有兩個(gè)結(jié)構(gòu)體属韧,struct __block_impl impl 與 struct __main_block_desc_0 *Desc

__block_impl結(jié)構(gòu)體的聲明為

struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr; 
};

存儲(chǔ)著一個(gè)isa安拟,指向一個(gè)存儲(chǔ)著該Block信息的地址
Flags 一個(gè)標(biāo)志
Reserved 該Block升級(jí)后所存放的區(qū)域
void *FuncPtr 一個(gè)無(wú)返回值的函數(shù)指針,指向由當(dāng)前Block語(yǔ)法指向的C語(yǔ)言函數(shù)指針

__main_block_desc_0結(jié)構(gòu)體的聲明為:

struct __main_block_desc_0 {
    unsigned long reserved;
    unsigned long Block_size;
}

這個(gè)結(jié)構(gòu)體保存今后版本升級(jí)所需的區(qū)域和Block的大小

關(guān)于__main_block_impl_0結(jié)構(gòu)體的構(gòu)造函數(shù)為

__main_block_impl_0 (void *fp, struct __main_block_desc_0 *desc, int flags = 0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
}

注意! 這里用到了結(jié)構(gòu)體 __block_impl
isa宵喂,F(xiàn)lags糠赦,F(xiàn)uncPtr都存儲(chǔ)在struct __block_impl 中

該構(gòu)造函數(shù)的調(diào)用:

void (*blk)(void) = (void (*)(void)) &__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA);

去掉轉(zhuǎn)換部分,可簡(jiǎn)化為:
struct __main_block_impl_0 tmp = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
struct __main_block_impl_0 *blk = &tmp;

意思為將棧上生成的__main_block_impl_0結(jié)構(gòu)體實(shí)例的指針賦值給__main_block_impl_0結(jié)構(gòu)體指針類型變量blk
對(duì)應(yīng)于我們書(shū)寫(xiě)的語(yǔ)句: void (^blk)(void) = ^{printf(“Block\n”);};
將Block語(yǔ)法生成的Block賦給Block類型變量blk
加個(gè)斷句锅棕,更加通俗一點(diǎn)的說(shuō)法:將 Block語(yǔ)法 生成 的Block 賦給 Block類型 變量 blk
等同于將__main_block_impl_0結(jié)構(gòu)體實(shí)例的指針賦給變量blk

__main_block_impl_0結(jié)構(gòu)體實(shí)例構(gòu)造參數(shù):

__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);

第一個(gè)參數(shù)是由Block語(yǔ)法轉(zhuǎn)換的C語(yǔ)言函數(shù)指針
第二個(gè)參數(shù)是作為靜態(tài)全局變量初始化的__main_block_desc_0結(jié)構(gòu)體實(shí)例指針

__main_block_desc_0結(jié)構(gòu)體實(shí)例的初始化部分代碼

static struct __main_block_desc_0 __main_block_desc_0_DATA = {
    0,
    sizeof(struct __main_block_impl_0)
};

由此可知拙泽,該源代碼使用Block,也就是結(jié)構(gòu)體__main_block_impl_0實(shí)例的大小進(jìn)行初始化

接下來(lái)再看看棧上的__main_block_impl_0 結(jié)構(gòu)體實(shí)例(即Block)是如何根據(jù)這些參數(shù)初始化的,先看下該結(jié)構(gòu)體:

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
}

優(yōu)先展開(kāi)struct __block_impl后裸燎,可轉(zhuǎn)換為

struct __main_block_impl_0 {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
    struct __main_block_desc_0 * Desc;
}

通常初始化的方式為:

isa = &_NSConcreteStackBlock;
Flags = 0;
Reserved = 0;
FuncPtr = __main_block_func_0;
Desc = &__main_block_desc_0_DATA;

相應(yīng)的作用之前已說(shuō)過(guò)顾瞻,稍后還會(huì)再說(shuō)一次。這里就不贅述德绿,先往下看吧荷荤。

接下來(lái)看一下調(diào)用此實(shí)例的部分,也就是我們書(shū)寫(xiě)的blk()脆炎;這一行代碼梅猿,轉(zhuǎn)化成以下源碼:

((void (*)(struct __block_impl *))((struct __block_impl *)blk)->FuncPtr)((struct __block_impl *)blk);

去掉轉(zhuǎn)換部分:

(*blk -> impl.FuncPtr)(blk);

簡(jiǎn)單的使用函數(shù)指針調(diào)用函數(shù)氓辣,入?yún)閎lk秒裕。
如剛才所說(shuō),由Block語(yǔ)法轉(zhuǎn)換的__main_block_func_0 函數(shù)的指針被賦值給blk的struct __block_impl的成員變量void * FuncPtr中钞啸。
同時(shí)也說(shuō)明了几蜻,__main_block_func_0函數(shù)的參數(shù)__cself指向Block值(blk)(__cself形同OC中的self喇潘,指自身。所以__cself指函數(shù)__main_block_func_0)梭稚。
到目前為止也可以看出Block(blk)是作為參數(shù)進(jìn)行傳遞颖低。

到此,我們來(lái)完整的總結(jié)一下關(guān)于一個(gè)無(wú)參無(wú)反弧烤,只輸出一句話的Block

int main(){
    
    void (^blk)(void) = ^{
        printf("Block");
    };
    blk();
}

通過(guò)clang -rewrite-objc 轉(zhuǎn)換成C語(yǔ)言后的源碼摘取部分:

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

        printf("Block");
    }

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

int main(){

    void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
}

在main函數(shù)中忱屑,會(huì)調(diào)用

 void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));

可簡(jiǎn)化為:

struct __main_block_impl_0 tmp = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
struct __main_block_impl_0 *blk = &tmp;

將棧上生成的__main_block_impl_0結(jié)構(gòu)體實(shí)例的指針賦值給__main_block_impl_0結(jié)構(gòu)體指針類型變量blk
而生成__main_block_impl_0結(jié)構(gòu)體實(shí)例的第一個(gè)參數(shù)__main_block_func_0,是一個(gè)是由Block語(yǔ)法轉(zhuǎn)換的C語(yǔ)言函數(shù)指針暇昂,內(nèi)部就是我們書(shū)寫(xiě)的那句輸出語(yǔ)句 printf(“Block”);
而第二個(gè)參數(shù)&__main_block_desc_0_DATA是struct __main_block_desc_0實(shí)例地址(該實(shí)例的指針?biāo)赶虻牡刂?
__main_block_desc_0_DATA結(jié)構(gòu)體的初始化是

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

第一個(gè)參數(shù)是今后升級(jí)所需的區(qū)域莺戒,為何是0不清楚。
第二個(gè)參數(shù)是Block的大小
struct __main_block_impl_0 相當(dāng)于當(dāng)前block急波,可將此結(jié)構(gòu)體中的struct __block_impl impl展開(kāi)為:

struct __main_block_impl_0 {
    void *isa; 
    int Flags;
    int Reserved;
    void *FuncPtr;
    struct __main_block_desc_0 * Desc;
}
通常初始化值為:
  isa = &_NSConcreteStackBlock;
  Flags = 0;
  Reserved = 0;
  FuncPtr = __main_block_func_0;
  Desc = &__main_block_desc_0_DATA;

isa指針指向的地址放置該Block的信息从铲,形同OC對(duì)象中的元類
Flags 標(biāo)志
Reserved 今后版本升級(jí)所存放的區(qū)域
FuncPtr是一個(gè)函數(shù)指針,指向__main_block_func_0 也就是當(dāng)前Block語(yǔ)法轉(zhuǎn)換的C語(yǔ)言函數(shù)指針
Desc就是struct __main_block_desc_0 實(shí)例澄暮,存放struct今后升級(jí)所需的區(qū)域與Block的大小

而后是main函數(shù)中的第二行

  ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
  去掉轉(zhuǎn)換部分
  (*blk -> impl.FuncPtr)(blk);

簡(jiǎn)單的使用函數(shù)指針調(diào)用結(jié)構(gòu)體中的函數(shù)名段,入?yún)閎lk
在函數(shù)指針FuncPtr指向的函數(shù)中調(diào)用printf(“Block”);
完成Block輸出

本次探討到此結(jié)束泣懊,預(yù)知后事如何伸辟,且聽(tīng)下回分解。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末馍刮,一起剝皮案震驚了整個(gè)濱河市自娩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌渠退,老刑警劉巖忙迁,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異碎乃,居然都是意外死亡姊扔,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)梅誓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)恰梢,“玉大人,你說(shuō)我怎么就攤上這事梗掰∏堆裕” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵及穗,是天一觀的道長(zhǎng)摧茴。 經(jīng)常有香客問(wèn)我,道長(zhǎng)埂陆,這世上最難降的妖魔是什么苛白? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任娃豹,我火速辦了婚禮,結(jié)果婚禮上购裙,老公的妹妹穿的比我還像新娘懂版。我一直安慰自己,他們只是感情好躏率,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布躯畴。 她就那樣靜靜地躺著,像睡著了一般薇芝。 火紅的嫁衣襯著肌膚如雪私股。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,262評(píng)論 1 308
  • 那天恩掷,我揣著相機(jī)與錄音倡鲸,去河邊找鬼。 笑死黄娘,一個(gè)胖子當(dāng)著我的面吹牛峭状,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播逼争,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼优床,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了誓焦?” 一聲冷哼從身側(cè)響起胆敞,我...
    開(kāi)封第一講書(shū)人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎杂伟,沒(méi)想到半個(gè)月后移层,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赫粥,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年观话,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片越平。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡频蛔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出秦叛,到底是詐尸還是另有隱情晦溪,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布挣跋,位于F島的核電站三圆,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嫌术,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望牌借。 院中可真熱鬧度气,春花似錦、人聲如沸膨报。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)现柠。三九已至院领,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間够吩,已是汗流浹背比然。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留周循,地道東北人强法。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像湾笛,于是被迫代替她去往敵國(guó)和親饮怯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • Blocks Blocks Blocks 是帶有局部變量的匿名函數(shù) 截取自動(dòng)變量值 int main(){ ...
    南京小伙閱讀 934評(píng)論 1 3
  • Block基礎(chǔ)回顧 1.什么是Block嚎研? 帶有局部變量的匿名函數(shù)(名字不重要蓖墅,知道怎么用就行),差不多就與C語(yǔ)言...
    Bugfix閱讀 6,771評(píng)論 5 61
  • 摘要block是2010年WWDC蘋(píng)果為Objective-C提供的一個(gè)新特性临扮,它為我們開(kāi)發(fā)提供了便利论矾,比如GCD...
    西門(mén)吹雪123閱讀 920評(píng)論 0 4
  • 前言 Blocks是C語(yǔ)言的擴(kuò)充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了這...
    小人不才閱讀 3,773評(píng)論 0 23
  • 2.1 Blcoks概要 2.1.1 什么是Blocks Blocks是C語(yǔ)言的擴(kuò)充功能——“帶有自動(dòng)變量(即局部...
    SkyMing一C閱讀 2,345評(píng)論 6 18