自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

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

移動(dòng)開發(fā) iOS
終于有空開始這系列最后一篇的編寫。這一篇,我們將看到block的內(nèi)存管理的內(nèi)部實(shí)現(xiàn),通過剖析runtime庫源碼,我們可以更深刻的理解block的內(nèi)存運(yùn)作體系。

看此篇時(shí),請(qǐng)大家同時(shí)打開兩個(gè)網(wǎng)址(或者下載它們到本地然后打開):

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

內(nèi)存管理的真面目

objc層面如何區(qū)分不同內(nèi)存區(qū)的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]; 

其用于對(duì)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. }; 

在棧上創(chuàng)建的block,其isa指針是_NSConcreteStackBlock。

2.全局區(qū)

在全局區(qū)創(chuàng)建的block,其比較類似,其構(gòu)造函數(shù)會(huì)將isa指針賦值為_NSConcreteGlobalBlock。

3.堆

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

  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復(fù)制得來的新block,其isa指針會(huì)被賦值為_NSConcreteMallocBlock

4.其余的isa類型

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

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

復(fù)制block

 對(duì)block調(diào)用Block_copy方法,或者向其發(fā)送objc copy消息,最終都會(huì)調(diào)用runtime.c中的_Block_copy_internal函數(shù),其內(nèi)部實(shí)現(xiàn)會(huì)檢查block的flag,從而進(jìn)行不同的操作:

  1. static void *_Block_copy_internal(const void *arg, const int flags) { 
  2.     ... 
  3.     aBlock = (struct Block_layout *)arg; 
  4.     ... 
  5. }1.棧block的復(fù)制 
  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指針的值之外,拷貝過程中,還會(huì)將BLOCK_NEEDS_FREE置入,大家記住這個(gè)值,后面會(huì)用到。

***,如果block有輔助copy/dispose函數(shù),那么輔助的copy函數(shù)會(huì)被調(diào)用。

2.全局block的復(fù)制

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

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

3.堆block的復(fù)制

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

棧block復(fù)制時(shí),置入的BLOCK_NEEDS_FREE標(biāo)記此時(shí)起作用,_Block_copy_internal函數(shù)識(shí)別當(dāng)前block是一個(gè)堆block,則僅僅增加引用計(jì)數(shù),然后返回原block。

輔助copy/dispose函數(shù)

1.普通變量的復(fù)制

輔助copy函數(shù)用于拷貝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的轉(zhuǎn)碼:

  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. };//所以我們知道,當(dāng)此結(jié)構(gòu)體被類型強(qiáng)轉(zhuǎn)為Block_byref時(shí),前四個(gè)成員是一致的,訪問flags就相當(dāng)于訪問__flags,而內(nèi)部實(shí)現(xiàn)就是這樣使用的 
  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初始化時(shí)__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*/);} 

此時(shí),復(fù)制時(shí)調(diào)用的輔助函數(shù):

  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) {//當(dāng)初次拷貝i時(shí),flags為0,進(jìn)入此分支會(huì)進(jìn)行復(fù)制操作并改變flags值,置入BLOCK_NEEDS_FREE和初始的引用計(jì)數(shù) 
  15.        ... 
  16.     } 
  17.     // already copied to heap 
  18.     else if ((src->forwarding->flags & BLOCK_NEEDS_FREE) == BLOCK_NEEDS_FREE) {//當(dāng)再次拷貝i時(shí),則僅僅增加其引用計(jì)數(shù) 
  19.         latching_incr_int(&src->forwarding->flags); 
  20.     } 
  21.     // assign byref data block pointer into new Block 
  22.     _Block_assign(src->forwarding, (void **)destp);//這句僅僅是直接賦值,其函數(shù)實(shí)現(xiàn)只有一行賦值語句,查閱runtime.c可知 

所以,我們知道,當(dāng)我們多次copy一個(gè)block時(shí),其引用的__block變量只會(huì)被拷貝一次。

2.objc變量的復(fù)制 

當(dāng)objc變量沒有__block修飾時(shí):

  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);//當(dāng)我們沒有開啟arc時(shí),這個(gè)函數(shù)會(huì)retian此object 
  6.         //printf("done retaining object at %p\n", object); 
  7.         _Block_assign((void *)object, destAddr); 
  8.     } 
  9.     .... 

當(dāng)objc變量有__block修飾時(shí):

  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告訴內(nèi)部實(shí)現(xiàn),這個(gè)變量結(jié)構(gòu)體具有自己的copy/dispose輔助函數(shù),而此時(shí)我們的內(nèi)部實(shí)現(xiàn)不會(huì)進(jìn)行默認(rèn)的復(fù)制操作:

  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.     } 

當(dāng)我們沒有開啟arc,且flags中具有BLOCK_BYREF_CALLER時(shí),會(huì)進(jìn)入_Block_assign函數(shù),而此函數(shù)僅僅是賦值

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

__block bSelf = self;

其他

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

我們已經(jīng)理解了非arc非gc情況下的block的內(nèi)存管理內(nèi)部實(shí)現(xiàn),對(duì)arc和gc的情況,其行為也是類似的,只是一些函數(shù)的指針指向的真正函數(shù)會(huì)改變,比如_Block_use_GC函數(shù),會(huì)將一些函數(shù)指向其他的實(shí)現(xiàn),使其適用于gc開啟的情況。

小結(jié)

block實(shí)際上是一些執(zhí)行語句和語句需要的上下文的組合,而runtime給予的內(nèi)部實(shí)現(xiàn)決定了它不會(huì)浪費(fèi)一比特的內(nèi)存。

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

責(zé)任編輯:閆佳明 來源: dreamingwish
相關(guān)推薦

2013-07-19 14:00:13

iOS中BlockiOS開發(fā)學(xué)習(xí)

2015-08-20 13:43:17

NFV網(wǎng)絡(luò)功能虛擬化

2015-09-08 10:06:15

2010-05-26 19:12:41

SVN沖突

2021-07-28 21:49:01

JVM對(duì)象內(nèi)存

2010-05-17 09:13:35

2021-06-07 08:18:12

云計(jì)算云端阿里云

2014-03-12 11:11:39

Storage vMo虛擬機(jī)

2009-06-01 09:04:44

Google WaveWeb

2010-05-11 10:19:17

VMforceJava云計(jì)算

2018-03-01 09:33:05

軟件定義存儲(chǔ)

2023-11-02 09:55:40

2016-04-06 09:27:10

runtime解密學(xué)習(xí)

2009-09-15 15:34:33

Google Fast

2012-08-17 09:27:34

奧運(yùn)會(huì)云計(jì)算虛擬化

2024-11-11 16:36:41

2024-02-14 09:00:00

機(jī)器學(xué)習(xí)索引ChatGPT

2015-09-06 13:40:02

HTTP網(wǎng)絡(luò)協(xié)議

2015-09-07 13:52:04

2016-11-16 09:06:59

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)