Block內(nèi)部的數(shù)據(jù)結(jié)構(gòu)類型

本文會(huì)記錄下最近對(duì)B lock 的一些探究,先從 block 是如何對(duì)局部變量捕獲開始講起.

image.png

上邊的代碼 auto變量的值改變時(shí),block 內(nèi)部輸出結(jié)果還是10,而 static 的局部變量 值變成了50. 那么 block 內(nèi)部是如何對(duì)age height 進(jìn)行處理的呢?

可以通過 xcrun -sdk phones clang -arch arm64 -rewrite-objc main.c -o main.cpp 將OC代碼轉(zhuǎn)成C++代碼實(shí)現(xiàn)

void testBlock() {
    auto int age = 10;
    static int height = 100;
    void(*block)(void) = &__testBlock_block_impl_0(
                                                   &__testBlock_block_func_0,
                                                   &__testBlock_block_desc_0_DATA, age, &height));
    age = 20;
    height = 50;
    block->FuncPtr(block);
    
    __testBlock_block_func_0  block實(shí)現(xiàn)部分的代碼
    __testBlock_block_desc_0_DATA block 內(nèi)存占用大小 描述信息
    
    __testBlock_block_impl_0 構(gòu)造方法 返回一個(gè)  __testBlock_block_impl_0 結(jié)構(gòu)體對(duì)象 并且取地址 復(fù)制給 *block 變量
    block->FuncPtr 實(shí)質(zhì)就是 block->impl.FuncPtr 由于結(jié)構(gòu)體內(nèi)部第一個(gè)成員就是該結(jié)構(gòu)體的內(nèi)存地址 ,所以 __testBlock_block_impl_0 可以看成 __block_impl 因?yàn)樗闹羔樀刂肪褪侵赶虻谝粋€(gè)成員 __block_impl的地址 ,所以通過強(qiáng)制轉(zhuǎn)換后 可以  block->FuncPtr(block); 調(diào)用 block
}

上邊的代碼還可以簡(jiǎn)化為


void testBlock() {
    auto int age = 10;
    static int height = 100;
    void(*block)(void) = &__testBlock_block_impl_0(__testBlock_block_func_0, &__testBlock_block_desc_0_DATA, age, &height));
    age = 20;
    height = 50;
  block->FuncPtr(block);  調(diào)用 block對(duì)應(yīng)的函數(shù)指針 

__testBlock_block_func_0 ///block 代碼的實(shí)現(xiàn) 相當(dāng)于一個(gè)函數(shù)變量

}

我們寫的block 最終被轉(zhuǎn)成一個(gè) struct __testBlock_block_impl_0 結(jié)構(gòu)體 ,并將 block 快代碼方法實(shí)現(xiàn) 封裝到一個(gè) __block_impl 結(jié)構(gòu)體里的 void *FuncPtr;
以下對(duì) block 的數(shù)據(jù)類型進(jìn)行詳細(xì)的說明

struct __testBlock_block_impl_0 {
  struct __block_impl impl; ///block函數(shù)回調(diào) 和 調(diào)用環(huán)境
  struct __testBlock_block_desc_0* Desc; ///描述了 block 占用多少內(nèi)存
  int age; ///對(duì)局部變量age的捕獲
  int *height;  ///對(duì)局部變量height的捕獲
///結(jié)構(gòu)體構(gòu)造方法 會(huì)對(duì)結(jié)構(gòu)體內(nèi)部所有成員進(jìn)行賦值 相當(dāng)于 init方法 
  __testBlock_block_impl_0(void *fp, struct __testBlock_block_desc_0 *desc, int _age, int *_height, int flags=0) : age(_age), height(_height) {
    impl.isa = &_NSConcreteStackBlock; ///block 內(nèi)部 isa 指針 block 實(shí)質(zhì)就是一個(gè)OC對(duì)象 封裝了函數(shù)調(diào)用和函數(shù)調(diào)用的環(huán)境 
    impl.Flags = flags;
    impl.FuncPtr = fp; /// 函數(shù)指針 我們 block 里邊的實(shí)現(xiàn)代碼 都保存到這個(gè)函數(shù)地址里 ,將來(lái)執(zhí)行 block 代碼塊 就是執(zhí)行函數(shù)指針指向的函數(shù)實(shí)現(xiàn)
    Desc = desc; ///對(duì) block 大小的描述
  }
};

auto變量 隨用隨開用完即銷 因此 block 捕獲 auto 變量時(shí) 只把值傳遞進(jìn)去進(jìn)來(lái)了保存 ,當(dāng) auto 變量銷毀的時(shí)候 也不影響 block 內(nèi)部對(duì)這個(gè) auto 變量的使用.

static局部變量 并不隨著函數(shù)作用域結(jié)束而銷毀 它會(huì)一直存在內(nèi)存中直到程序聲明周期結(jié)束才釋放掉,因此 block 對(duì)局部靜態(tài)變量的捕獲是以指針地址進(jìn)行傳遞,由于是指針賦值而不是值傳遞 所以 height 改變時(shí) block 內(nèi)部輸出的值時(shí)最新的 height 的值.

這個(gè)函數(shù)封裝了 block代碼的實(shí)現(xiàn) 即 ^{
NSLog(@"age = %d height = %d",age,height);
};
由FuncPtr 函數(shù)指針調(diào)用該函數(shù)


static void __testBlock_block_func_0(struct __testBlock_block_impl_0 *__cself) {
  int age = __cself->age; // bound by copy
  int *height = __cself->height; // bound by copy

  NSLog((NSString *)&__NSConstantStringImpl__var_folders_hj_pwgsq9614nb0vq4zd315tcx80000gn_T_main_a95244_mi_0,age,(*height));
 }

還可以將 block 強(qiáng)制轉(zhuǎn)換成 __block_impl 結(jié)構(gòu)體 進(jìn)行函數(shù)的調(diào)用

image.png

輸出結(jié)果跟 block() 是一樣的

由于 block 內(nèi)部會(huì)對(duì)局部變量進(jìn)行捕獲 所以就會(huì)造成循環(huán)引用的問題 ,比如 self 持有 block ,而 block 內(nèi)部會(huì)使用到 self ,這種情況下就會(huì)造成 block 和OC對(duì)象內(nèi)存泄露問題

image.png

OC的方法能夠使用到 self _cmd 是因?yàn)橛袃蓚€(gè) 匿名參數(shù) id self , SEL _cmd ,所以 self 在方法里邊也是一個(gè)局部的變量 這個(gè)時(shí)候 block 就會(huì)捕獲這個(gè) self ,而 self 又持有 block 因此造成了循環(huán)引用 我們通過C++代碼一探究竟

image.png
image.png

可以看出來(lái) block 內(nèi)部有一個(gè)Person *self 的指針 , 在給 block 賦值的時(shí)候 將 self 的內(nèi)存地址傳遞進(jìn)去 ,這樣就造成了 互相持有,所以 block 使用過程中需要注意循環(huán)引用的問題

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鳍征,一起剝皮案震驚了整個(gè)濱河市兰英,隨后出現(xiàn)的幾起案子婴谱,更是在濱河造成了極大的恐慌瞬逊,老刑警劉巖送爸,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件朝聋,死亡現(xiàn)場(chǎng)離奇詭異虐沥,居然都是意外死亡腿宰,警方通過查閱死者的電腦和手機(jī)雁刷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門覆劈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說我怎么就攤上這事墩崩∶ビⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵鹦筹,是天一觀的道長(zhǎng)铝阐。 經(jīng)常有香客問我,道長(zhǎng)铐拐,這世上最難降的妖魔是什么徘键? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮遍蟋,結(jié)果婚禮上吹害,老公的妹妹穿的比我還像新娘。我一直安慰自己虚青,他們只是感情好它呀,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著棒厘,像睡著了一般纵穿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上奢人,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天谓媒,我揣著相機(jī)與錄音,去河邊找鬼何乎。 笑死句惯,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的支救。 我是一名探鬼主播抢野,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼各墨!你這毒婦竟也來(lái)了蒙保?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤欲主,失蹤者是張志新(化名)和其女友劉穎邓厕,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扁瓢,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡详恼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了引几。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片昧互。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡挽铁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出敞掘,到底是詐尸還是另有隱情叽掘,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布玖雁,位于F島的核電站更扁,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏赫冬。R本人自食惡果不足惜浓镜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望劲厌。 院中可真熱鬧膛薛,春花似錦、人聲如沸补鼻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)风范。三九已至增淹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間乌企,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工成玫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留加酵,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓哭当,卻偏偏與公主長(zhǎng)得像猪腕,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子钦勘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

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