iOS中Block介紹(二)內存管理與其他特性

iOS中Block介紹(二)內存管理與其他特性

2013-07-19 13:16 佚名 dreamingwish 字號:T | T

一鍵收藏,隨時查看,分享好友!

我們在前一章介紹了block的用法,而正確使用block必須要求正確理解block的內存管理問題。這一章,我們只陳述結果而不追尋原因,我們將在下一章深入其原因。

AD:WOT2015 互聯網運維與開發者大會 熱銷搶票

一、block放在哪裏

我們針對不同情況來討論block的存放位置:

1.棧和堆

以下情況中的block位於堆中:

void foo()  {      __block int i = 1024;      int j = 1;      void (^blk)(void);      void (^blkInHeap)(void);      blk = ^{ printf("%d, %d\n", i, j);};//blk在棧裏      blkInHeap = Block_copy(blk);//blkInHeap在堆裏  }     - (void)fooBar  {      _oi = 1;      OBJ1* oj = self;      void (^oblk)(void) = ^{ printf("%d\n", oj.oi);};      void (^oblkInHeap)(void) = [oblk copy];//oblkInHeap在堆中  }

2.全局區

以下情況中的block位於全局區:

static int(^maxIntBlock)(int, int) = ^(int a, int b){return a>b?a:b;};  - (void)fooBar  {       int(^maxIntBlockCopied)(int, int) =[maxIntBlock copy];  }  void foo()  {       int(^maxIntBlockCopied)(int, int) = Block_copy(maxIntBlock);  }

需要注意的是,這裏複製過後的block依舊位於全局區,實際上,複製操作是直接返回了原block對象。

二、block引用的變量在哪裏

 1.全局區

全局區的變量存儲位置與block無關:

static int gVar = 0;  //__block static int gMVar = 1;  void foo()  {      static int stackVar = 0;  //    __block static int stackMVar = 0;  }

注意:static變量是不允許添加__block標記的

2.堆棧 

此時,你可能會問,當函數foo返回後,棧上的j已經回收,那麼blkInHeap怎麼能繼續使用它?這是因爲沒有__block標記的變量,會被當做實參傳入block的底層實現函數中,當block中的代碼被執行時,j已經不是原來的j了,所謂物是人非就是這樣吧~

另外,如果使用到變量j的所有block都沒有被複制至heap,那麼這個變量j也不會被複制至heap。

因此,即使將j++這一句放到blk()這句之前,這段代碼執行後,控制檯打印結果也是:1024, 1。而不是1024, 2

三、其他特性

1.複製的行爲

對block調用複製,有以下幾種情況:

1.對全局區的block調用copy,會返回原指針,並且這期間不處理任何東西(至少目前的內部實現是這樣);

2.對棧上的block調用copy,每次會返回新複製到堆上的block的指針,同時,所有__block變量都會被複制至堆一份(多次拷貝,只會生成一份)。

3.對已經位於heap上的block,再次調用copy,只會增加block的引用計數。

爲什麼我們不討論retian的行爲?原因是並沒有Block_retain()這樣的函數,而且objc裏面的retain消息發送給block對象後,其內部實現是什麼都不做。

2.objc類中的block複製

objc類實例方法中的block如果被複制至heap,那麼當前實例會被增加引用計數,當這個block被釋放時,此實例會被減少引用計數。

但如果這個block沒有使用當前實例的任何成員,那麼當前實例不會被增加引用計數。這也是很自然的道理,我既然沒有用到這個instance的任何東西,那麼我幹嘛要retian它?

我們要注意的一點是,我看到網上有很多人說block引起了實例與block之間的循環引用(retain-cycle),並且給出解決方案:不直接使用self而先將self賦值給一個臨時變量,然後再使用這個臨時變量。

但是,大家注意,我們一定要爲這個臨時變量增加__block標記(多謝第三篇文章回帖網友的提醒)。

這一章我們以結果導向的方式來說明了各種情況下,block的內存問題,下一章,我將剖析運行時庫的源碼,從根源闡述block的行爲。也就是過程導向的方式了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章