block的那些事兒,block事兒

來源:互聯網
上載者:User

block的那些事兒,block事兒

這裡的部落格已經不怎麼維護了,建議你來這裡看我寫的部落格。如果發現部落格寫的有問題,歡迎指正。如果希望討論iOS技術,歡迎加我的QQ群:159974494

2015年3月2日,新年的第二篇部落格。

這篇部落格適合對block有一定的瞭解的人閱讀,如果你對還不知道什麼是block,那也許這篇文章更適合你。

block實現

假設你對Block已經有了一定瞭解。首先對於block,我們看看他到底是怎麼實現的。

int main() {    __block id obj = [NSObject new];    void (^blk)(void) = ^{        NSLog(@"%@",obj);    };    return 1;}

對於這樣一個block,我們使用clang工具將a.m(代碼的檔案)編譯為C語言

clang -rewrite-objc a.m

找到a.cpp檔案,裡面的代碼有很多。首先我們來看block的定義

/** block 對象(結構體) */struct __main_block_impl_0 {    void *isa;  //Class指標,指向block的Class    int Flags;  //用於儲存block引用計數,是堆上還是棧上或者全域區,是否有拷貝函數等資訊    int Reserved;   //保留變數    void *FuncPtr;  //函數指標,指向block的實現    struct __main_block_desc_0* Desc;   //block的附加資訊    __Block_byref_obj_0 *obj;   //捕獲的變數};

這個裡面有兩個地方不是太清楚,__main_block_desc_0__Block_byref_obj_0,下面我們來看看

/** block的附加資訊 */static struct __main_block_desc_0 {    size_t reserved;    //保留變數    size_t Block_size;  //block 的大小    // copy和dispose函數。用於將block中捕獲的對象copy到堆上和釋放。    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);    void (*dispose)(struct __main_block_impl_0*);}

我們可以看到__main_block_desc_0是一個結構體,用於儲存block的附加資訊

我們再來看看__Block_byref_obj_0是個什麼東東?

struct __Block_byref_obj_0 {    void *__isa;  //Class指標    __Block_byref_obj_0 *__forwarding;  //保證block複製到堆上時,原來棧上block也能訪問到堆上的block    int __flags;   //使用位儲存一些資訊,包括捕獲的對象在棧上還是堆上    int __size;    //結構體大小    //記憶體管理用,copy和dispose    void (*__Block_byref_id_object_copy)(void*, void*);    void (*__Block_byref_id_object_dispose)(void*);    id obj; //捕獲的對象};

當我們對一個變數使用__block參數時,編譯器會自動將變數轉換成__Block_byref_obj_0對象。下面我們來看看__Block_byref_obj_0對象的具體細節

對於__forwarding指標說的不是太清楚,下面我來單獨說說。

當block產生時,__forwarding指向自身

當block從棧上copy到堆上時,棧上的__Block_byref_obj_0的__forwarding指向堆上的__Block_byref_obj_0。堆上的__Block_byref_obj_0的__forwarding指向自身

當block從棧上copy到堆上後,可能棧上的block和堆上的block同時都在使用(某些地方只能拿到棧上的block),如果直接使用__Block_byref_obj_0本身,將會導致資料不同步的問題,所以代碼所有地方都不直接使用__Block_byref_obj_0,而是使用__Block_byref_obj_0的__forwarding指標。

另外對於Block的捕獲關係希望大家弄清楚:
1. block(__main_block_impl_0)捕獲__block(__Block_byref_obj_0)obj
2. __block(__Block_byref_obj_0)捕獲對象id obj

block的一些問題1.block有幾種類型

block主要分為三種Block:

2.block copy做了什嗎?

根據block不同類型,copy分為三種情況:

3.block的循環參考怎麼避免4.非ARC下,__block為什麼能避免循環參考

首先我們來看迴圈應用的原因。一般情況下,self持有block,block又持有self,照成循環參考。iOS中的持有是指引用計數+1.
1. 由於block是self的屬性,block產生時,必然會引用計數+1,所以self持有block。
2. 由於block要使用必須要copy到堆上,否則棧上block誇地區使用會崩潰。block對象中捕獲了self作為屬性,當block copy到堆上時,調用block調用__main_block_desc_0中的copy函數,使self引用計數+1.所以block持有self

當使用__block屬性時,block捕獲__Block_byref_obj_0對象,__Block_byref_obj_0對象捕獲self。block copy時,使得__Block_byref_obj_0對象的retainCount+1,block持有__Block_byref_obj_0對象,然後self的引用計數不會增加,__Block_byref_obj_0對象不會持有self。所以block不持有self,打破循環參考

5.ARC下,__block為什麼不能避免循環參考
__block id blockSelf = self;    //① 這裡是block持有self的關鍵void (^blk)(void) = ^{          //② ARC不會釋放blockSelf    NSLog(@"%@",blockSelf);};

我們看上面的代碼,在ARC下,由於blockSelf預設為__strong類型,所以①位置處retainCount+1,blockSelf持有self。而block中捕獲了blockSelf,只要block存在,系統就不會釋放blockSelf。間接導致block持有self。
非ARC下由於①處不會使retainCount+1,所以能避免block持有self。

參考

唐巧大神的部落格

Objective-C進階編程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.