成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

iOS中block介紹(四)揭開神秘面紗(下)

移動開發 iOS
終于有空開始這系列最后一篇的編寫。這一篇,我們將看到block的內存管理的內部實現,通過剖析runtime庫源碼,我們可以更深刻的理解block的內存運作體系。

看此篇時,請大家同時打開兩個網址(或者下載它們到本地然后打開):

http://llvm.org/svn/llvm-project/compiler-rt/trunk/BlocksRuntime/runtime.c

http://llvm.org/svn/llvm-project/compiler-rt/trunk/BlocksRuntime/Block_private.h

內存管理的真面目

objc層面如何區分不同內存區的block

Block_private.h中有這樣一組值:

  1. /* the raw data space for runtime classes for blocks */ 
  2. /* class+meta used for stack, malloc, and collectable based blocks */ 
  3. BLOCK_EXPORT void * _NSConcreteStackBlock[32]; 
  4. BLOCK_EXPORT void * _NSConcreteMallocBlock[32]; 
  5. BLOCK_EXPORT void * _NSConcreteAutoBlock[32]; 
  6. BLOCK_EXPORT void * _NSConcreteFinalizingBlock[32]; 
  7. BLOCK_EXPORT void * _NSConcreteGlobalBlock[32]; 
  8. BLOCK_EXPORT void * _NSConcreteWeakBlockVariable[32]; 

其用于對block的isa指針賦值

1.棧

  1. struct __OBJ1__of2_block_impl_0 { 
  2.   struct __block_impl impl; 
  3.   struct __OBJ1__of2_block_desc_0* Desc; 
  4.   OBJ1 *self; 
  5.   __OBJ1__of2_block_impl_0(void *fp, struct __OBJ1__of2_block_desc_0 *desc, OBJ1 *_self, int flags=0) : self(_self) { 
  6.     impl.isa = &_NSConcreteStackBlock; 
  7.     impl.Flags = flags; 
  8.     impl.FuncPtr = fp; 
  9.     Desc = desc; 
  10.   } 
  11. }; 

在棧上創建的block,其isa指針是_NSConcreteStackBlock。

2.全局區

在全局區創建的block,其比較類似,其構造函數會將isa指針賦值為_NSConcreteGlobalBlock。

3.堆

我們無法直接創建堆上的block,堆上的block需要從stack block拷貝得來,在runtime.c中的_Block_copy_internal函數中,有這樣幾行:

  1. // Its a stack block.  Make a copy. 
  2.     if (!isGC) { 
  3.         struct Block_layout *result = malloc(aBlock->descriptor->size); 
  4.         ... 
  5.         result->isa = _NSConcreteMallocBlock; 
  6.         ... 
  7.         return result; 
  8.     } 

可以看到,棧block復制得來的新block,其isa指針會被賦值為_NSConcreteMallocBlock

4.其余的isa類型

  1. BLOCK_EXPORT void * _NSConcreteAutoBlock[32]; 
  2. BLOCK_EXPORT void * _NSConcreteFinalizingBlock[32]; 
  3. BLOCK_EXPORT void * _NSConcreteWeakBlockVariable[32]; 

其他三種類型是用于gc和arc,我們暫不討論

復制block

 對block調用Block_copy方法,或者向其發送objc copy消息,最終都會調用runtime.c中的_Block_copy_internal函數,其內部實現會檢查block的flag,從而進行不同的操作:

  1. static void *_Block_copy_internal(const void *arg, const int flags) { 
  2.     ... 
  3.     aBlock = (struct Block_layout *)arg; 
  4.     ... 
  5. }1.棧block的復制 
  6.  
  7.         // reset refcount 
  8.         result->flags &= ~(BLOCK_REFCOUNT_MASK);    // XXX not needed 
  9.         result->flags |= BLOCK_NEEDS_FREE | 1; 
  10.         result->isa = _NSConcreteMallocBlock; 
  11.         if (result->flags & BLOCK_HAS_COPY_DISPOSE) { 
  12.             //printf("calling block copy helper %p(%p, %p)...\n", aBlock->descriptor->copy, result, aBlock); 
  13.             (*aBlock->descriptor->copy)(result, aBlock); // do fixup 
  14.         } 

 除了修改isa指針的值之外,拷貝過程中,還會將BLOCK_NEEDS_FREE置入,大家記住這個值,后面會用到。

***,如果block有輔助copy/dispose函數,那么輔助的copy函數會被調用。

2.全局block的復制

  1. else if (aBlock->flags & BLOCK_IS_GLOBAL) { 
  2.         return aBlock; 
  3.     }全局block進行copy是直接返回了原block,沒有任何的其他操作。

全局block進行copy是直接返回了原block,沒有任何的其他操作。

3.堆block的復制

  1. if (aBlock->flags & BLOCK_NEEDS_FREE) { 
  2.     // latches on high 
  3.     latching_incr_int(&aBlock->flags); 
  4.     return aBlock; 

棧block復制時,置入的BLOCK_NEEDS_FREE標記此時起作用,_Block_copy_internal函數識別當前block是一個堆block,則僅僅增加引用計數,然后返回原block。

輔助copy/dispose函數

1.普通變量的復制

輔助copy函數用于拷貝block所引用的可修改變量,我們這里以 __block int i = 1024為例:

先看看Block_private.h中的定義:

  1. struct Block_byref { 
  2.     void *isa; 
  3.     struct Block_byref *forwarding; 
  4.     int flags; /* refcount; */ 
  5.     int size; 
  6.     void (*byref_keep)(struct Block_byref *dst, struct Block_byref *src); 
  7.     void (*byref_destroy)(struct Block_byref *); 
  8.     /* long shared[0]; */ 
  9. }; 

而我們的__block int i = 1024的轉碼:

  1. struct __Block_byref_i_0 { 
  2.   void *__isa; 
  3. __Block_byref_i_0 *__forwarding; 
  4.  int __flags; 
  5.  int __size; 
  6.  int i; 
  7. };//所以我們知道,當此結構體被類型強轉為Block_byref時,前四個成員是一致的,訪問flags就相當于訪問__flags,而內部實現就是這樣使用的 
  8. ... 
  9. __attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 1024};//i初始化時__flags為0static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->i, (void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);} 

此時,復制時調用的輔助函數:

  1. void _Block_object_assign(void *destAddr, const void *object, const int flags) {//此處flags為8,即BLOCK_FIELD_IS_BYREF 
  2.     ... 
  3.     if ((flags & BLOCK_FIELD_IS_BYREF) == BLOCK_FIELD_IS_BYREF)  { 
  4.         // copying a __block reference from the stack Block to the heap 
  5.         // flags will indicate if it holds a __weak reference and needs a special isa 
  6.         _Block_byref_assign_copy(destAddr, object, flags); 
  7.     } 
  8.     ... 
  9.  
  10. static void _Block_byref_assign_copy(void *dest, const void *arg, const int flags) {//此處flags為8,即BLOCK_FIELD_IS_BYREF 
  11.     struct Block_byref **destp = (struct Block_byref **)dest; 
  12.     struct Block_byref *src = (struct Block_byref *)arg; 
  13.     ... 
  14.     else if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {//當初次拷貝i時,flags為0,進入此分支會進行復制操作并改變flags值,置入BLOCK_NEEDS_FREE和初始的引用計數 
  15.        ... 
  16.     } 
  17.     // already copied to heap 
  18.     else if ((src->forwarding->flags & BLOCK_NEEDS_FREE) == BLOCK_NEEDS_FREE) {//當再次拷貝i時,則僅僅增加其引用計數 
  19.         latching_incr_int(&src->forwarding->flags); 
  20.     } 
  21.     // assign byref data block pointer into new Block 
  22.     _Block_assign(src->forwarding, (void **)destp);//這句僅僅是直接賦值,其函數實現只有一行賦值語句,查閱runtime.c可知 

所以,我們知道,當我們多次copy一個block時,其引用的__block變量只會被拷貝一次。

2.objc變量的復制 

當objc變量沒有__block修飾時:

  1. static void __OBJ1__of2_block_copy_0(struct __OBJ1__of2_block_impl_0*dst, struct __OBJ1__of2_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}void _Block_object_assign(void *destAddr, const void *object, const int flags) { 
  2.     ... 
  3.     else if ((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT) { 
  4.         //printf("retaining object at %p\n", object); 
  5.         _Block_retain_object(object);//當我們沒有開啟arc時,這個函數會retian此object 
  6.         //printf("done retaining object at %p\n", object); 
  7.         _Block_assign((void *)object, destAddr); 
  8.     } 
  9.     .... 

當objc變量有__block修飾時:

  1. struct __Block_byref_bSelf_0 { 
  2.   void *__isa; 
  3. __Block_byref_bSelf_0 *__forwarding; 
  4.  int __flags; 
  5.  int __size; 
  6.  void (*__Block_byref_id_object_copy)(void*, void*); 
  7.  void (*__Block_byref_id_object_dispose)(void*); 
  8.  OBJ1 *bSelf; 
  9. }; 
  10. static void __Block_byref_id_object_copy_131(void *dst, void *src) { 
  11.  _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);//131即為BLOCK_FIELD_IS_OBJECT|BLOCK_BYREF_CALLER 
  12. static void __Block_byref_id_object_dispose_131(void *src) { 
  13.  _Block_object_dispose(*(void * *) ((char*)src + 40), 131); 
  14.   
  15. ... //33554432即為BLOCK_HAS_COPY_DISPOSE 
  16.     __block __Block_byref_bSelf_0 bSelf = {(void*)0,(__Block_byref_bSelf_0 *)&bSelf, 33554432, sizeof(__Block_byref_bSelf_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, self}; 

BLOCK_HAS_COPY_DISPOSE告訴內部實現,這個變量結構體具有自己的copy/dispose輔助函數,而此時我們的內部實現不會進行默認的復制操作:

  1. void _Block_object_assign(void *destAddr, const void *object, const int flags) { 
  2.     //printf("_Block_object_assign(*%p, %p, %x)\n", destAddr, object, flags); 
  3.     if ((flags & BLOCK_BYREF_CALLER) == BLOCK_BYREF_CALLER) { 
  4.         if ((flags & BLOCK_FIELD_IS_WEAK) == BLOCK_FIELD_IS_WEAK) { 
  5.             _Block_assign_weak(object, destAddr); 
  6.         } 
  7.         else { 
  8.             // do *not* retain or *copy* __block variables whatever they are 
  9.             _Block_assign((void *)object, destAddr); 
  10.         } 
  11.     } 

當我們沒有開啟arc,且flags中具有BLOCK_BYREF_CALLER時,會進入_Block_assign函數,而此函數僅僅是賦值

所以,如果要避免objc實例中的block引起的循環引用,我們需要讓block間接使用self:

__block bSelf = self;

其他

對于dipose輔助函數,其行為與copy是類似的,我們不再重復同樣的東西,如果大家要了解,自行查閱runtime.c和Block_private.h即可。

我們已經理解了非arc非gc情況下的block的內存管理內部實現,對arc和gc的情況,其行為也是類似的,只是一些函數的指針指向的真正函數會改變,比如_Block_use_GC函數,會將一些函數指向其他的實現,使其適用于gc開啟的情況。

小結

block實際上是一些執行語句和語句需要的上下文的組合,而runtime給予的內部實現決定了它不會浪費一比特的內存。

我們知道cocoa中的容器類class有mutable和immutable之分,實際上我們可以將block看做一個immutable的容器,其盛放的是執行的代碼和執行此代碼需要的變量,而一個immutable變量的無法改變的特質,也決定了block在復制時,的確沒有必要不斷分配新的內存。故而其復制的行為會是增加引用計數。

責任編輯:閆佳明 來源: dreamingwish
相關推薦

2013-07-19 14:00:13

iOS中BlockiOS開發學習

2015-08-20 13:43:17

NFV網絡功能虛擬化

2015-09-08 10:06:15

2010-05-26 19:12:41

SVN沖突

2021-07-28 21:49:01

JVM對象內存

2010-05-17 09:13:35

2021-06-07 08:18:12

云計算云端阿里云

2014-03-12 11:11:39

Storage vMo虛擬機

2009-06-01 09:04:44

Google WaveWeb

2010-05-11 10:19:17

VMforceJava云計算

2018-03-01 09:33:05

軟件定義存儲

2009-09-15 15:34:33

Google Fast

2016-04-06 09:27:10

runtime解密學習

2023-11-02 09:55:40

2012-08-17 09:27:34

奧運會云計算虛擬化

2024-02-14 09:00:00

機器學習索引ChatGPT

2015-09-06 13:40:02

HTTP網絡協議

2015-09-07 13:52:04

2024-11-11 16:36:41

2016-11-16 09:06:59

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产日韩精品在线 | 91精品国产91久久综合桃花 | 日韩一区二区免费视频 | 亚洲精品美女视频 | 盗摄精品av一区二区三区 | 国产成人福利视频在线观看 | 国产精品久久久久一区二区三区 | 国产精品久久久久久久久久久免费看 | 色婷婷精品国产一区二区三区 | 99福利在线观看 | 久久精品一区 | www.97国产 | www.国产 | 中文字幕在线看 | 在线一级片 | 成人不卡视频 | 亚洲精品电影在线观看 | 涩爱av一区二区三区 | 中文字幕亚洲视频 | 日本视频中文字幕 | 欧美日韩中文在线 | 一区二区三区精品 | 国产一区二区 | 日本不卡视频在线播放 | 日日操夜夜操视频 | 成人黄页在线观看 | www.97zyz.com| 午夜影视 | 精品美女久久久 | 欧美成人激情 | www国产亚洲精品 | 国产精品久久久久久二区 | av无遮挡 | 国产黄色网址在线观看 | 久久福利电影 | 日本一区二区三区四区 | 精品少妇一区二区三区在线播放 | 91麻豆精品国产91久久久更新资源速度超快 | 中文二区| 亚洲精品一区二区三区四区高清 | 久久久久久91香蕉国产 |