iOS---淺談Block的理解

Block 是什么集峦?
Block 是一個(gè)匿名的函數(shù),但是它能夠捕獲變量顺又,這是它跟匿名函數(shù)的區(qū)別更卒。

Block 是如何捕獲變量的,這個(gè)得要看一下 Block 的源代碼稚照,去理解Block 實(shí)現(xiàn)的原理蹂空。

Block的實(shí)現(xiàn)

struct __block_impl {
    // 根據(jù) Block 的不同位置自動生成的 Block 的類型
    void *isa;
    int Flags;
    int Reserved;
    // FuncPtr 指向 Block 的最終實(shí)現(xiàn)俯萌,即 __main_block_func_0
    void *FuncPtr;
    // 捕獲的變量
    int XXX
}

//Block的內(nèi)存管理
static struct __main_block_desc_0 {
    unsigned long reserved;
    unsigned long Block_size;
}

//__main_block_func_0 是 Block 的代碼
// __cself 為指向 Block 值的指針變量( _cself 類似于 self 的用法)
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    // 被截獲的局部變量
    //XXX= __ceself ->XXX
    
}

// Block 的對象,持有了 impl 與 Desc 結(jié)構(gòu)體
struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0 *Desc;
    // 被截獲的局部變量
     // Block 的構(gòu)造器上枕,...()為被截獲的局部變量咐熙,如val(_val)
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, _..., int flags = 0): ...() {
        impl.isa     = blockType(非參數(shù),根據(jù) Block 的不同位置自動生成的默認(rèn)值);
        impl.Flags   = flags;
        impl.FuncPtr = fp;
        Desc         = desc;       
    }
}


struct __main_block_impl_0 {
    // 根據(jù) Block 的不同位置自動生成的 Block 的類型
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
    unsigned long reserved;
    unsigned long Block_size;
    // 被截獲的局部變量
    // ...
}

Block 截獲變量的做法是把 Block 內(nèi)部需要用到的變量都追加到 Block 的構(gòu)造器上,通過__main_block_func_0內(nèi)部 XXX= _ceself ->XXX去訪問捕獲到的變量

問題一:為什么 Block 修改不了自動變量的值辨萍?
問題二:為什么 _block 變量可以被 Block 修改棋恼?

問題一相對容易理解一些

struct __block_impl {
    // 根據(jù) Block 的不同位置自動生成的 Block 的類型
    void *isa;
    int Flags;
    int Reserved;
    // FuncPtr 指向 Block 的最終實(shí)現(xiàn),即 __main_block_func_0
    void *FuncPtr;
    // 捕獲的變量
    int XXX
}

Block 捕獲變量 XXX 只是把 XXX 的值復(fù)制在 Block 锈玉,然后把復(fù)制的值追加到 Block 構(gòu)造器上爪飘。

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  // 被截獲的局部變量
  XXX= __ceself ->XXX
  XXX = a;
}

如果在 Block 對 XXX 就行修改拉背,其實(shí)只是修改復(fù)制在 __main_block_func_0的復(fù)制值师崎,并沒有影響到原來的 XXX。

如果想修改 XXX 的值椅棺,應(yīng)該拿到 XXX 的內(nèi)存地址犁罩,才能對XXX做修改。
Block 修改局部靜態(tài)變量就是這樣做的两疚。

問題三:為什么在 Block 修改自動變量不像修改局部靜態(tài)變量那樣做呢床估?

_block 的實(shí)現(xiàn)

_block變量不是直接追加到 Block ,而是會變成一個(gè)結(jié)構(gòu)體,然后加入在 Block 中。

//自動變量轉(zhuǎn)化成一個(gè)結(jié)構(gòu)體
struct __Block_byref_val_0 {
    void *__isa;
    // __forwarding 用于實(shí)現(xiàn)無論 __block 配置在棧上還是堆上都能夠正常訪問 __block 變量
    __Block_byte_val_0 *__forwarding;
    int __flags;
    int __size;
    int val;
}

// Block 持有自動變量結(jié)構(gòu)體指針
struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0 *Desc;
    __Block_byref_val_0 *val;

    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, 
                            __Block_byref_val_0 *_val, 
                            int flags = 0): val(val -> __forwarding) {
        impl.isa     = blockType(非參數(shù)诱渤,根據(jù) Block 的不同位置自動生成的默認(rèn)值);
        impl.Flags   = flags;
        impl.FuncPtr = fp;
        Desc         = desc;       
    }

//Block 的代碼實(shí)現(xiàn)
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
     //把變量的值修改成 a
    __Block_byref_val_0 *val = __cself -> val;
    (val -> __forwarding -> val) = a;
}

問題二的解答
Block 的__main_block_impl_0持有__Block_byref_val_0的指針
__Block_byref_val_0的成員變量__forwarding持有指向該實(shí)例自身的指針
通過__forwarding去訪問成員變量valval可以看作成自動變量)

4155930-f5ecf62514cdc762.jpg

通過上面的方式(實(shí)質(zhì)上還是通過訪問變量內(nèi)存地址的方式)去修改自動變量

問題三的解答
_block 變量變成結(jié)構(gòu)體丐巫,然后在追加到 Block 上,這樣是為了多個(gè) Block 可以訪問同一個(gè)自動變量源哩。所有Block 捕獲到該自動變量時(shí)鞋吉,加上該自動變量的結(jié)構(gòu)體就可以訪問到該自動變量鸦做。

Block的類型

  • NSConcreteGlobalBlock 存在數(shù)據(jù)區(qū)域励烦,不會捕獲自動變量,能引用全局變量泼诱,全局靜態(tài)變量坛掠。
  • NSConcreteStackBlock 存在棧中,當(dāng) Block 退出函數(shù)時(shí)治筒,Block 銷毀屉栓,捕獲的自動變量隨 Block 退出棧而清空。
  • NSConcreteMallocBlock 存在堆耸袜,Block 不會因?yàn)橥顺龊瘮?shù)而銷毀友多,捕獲的自動變量

Block 的 copy

StackBlock 通常會 copy 到堆上,這樣做就是為了避免 StackBlock 退出作用域而被銷毀堤框。_block 也會 copy 到堆上域滥,__forwarding的作用就是正確訪問到不論在棧還是在堆上的 val纵柿。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市启绰,隨后出現(xiàn)的幾起案子昂儒,更是在濱河造成了極大的恐慌,老刑警劉巖委可,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渊跋,死亡現(xiàn)場離奇詭異,居然都是意外死亡着倾,警方通過查閱死者的電腦和手機(jī)拾酝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卡者,“玉大人微宝,你說我怎么就攤上這事』⒄#” “怎么了蟋软?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長嗽桩。 經(jīng)常有香客問我岳守,道長,這世上最難降的妖魔是什么碌冶? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任湿痢,我火速辦了婚禮,結(jié)果婚禮上扑庞,老公的妹妹穿的比我還像新娘譬重。我一直安慰自己,他們只是感情好罐氨,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布臀规。 她就那樣靜靜地躺著,像睡著了一般栅隐。 火紅的嫁衣襯著肌膚如雪塔嬉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天租悄,我揣著相機(jī)與錄音谨究,去河邊找鬼。 笑死泣棋,一個(gè)胖子當(dāng)著我的面吹牛胶哲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播潭辈,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼鸯屿,長吁一口氣:“原來是場噩夢啊……” “哼俩檬!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起碾盟,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤棚辽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后冰肴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屈藐,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年熙尉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了联逻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,503評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡检痰,死狀恐怖包归,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情铅歼,我是刑警寧澤公壤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站椎椰,受9級特大地震影響厦幅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜慨飘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一确憨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瓤的,春花似錦休弃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至本辐,卻和暖如春桥帆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背慎皱。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留叶骨,地道東北人茫多。 一個(gè)月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像忽刽,于是被迫代替她去往敵國和親天揖。 傳聞我的和親對象是個(gè)殘疾皇子夺欲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評論 2 359

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