概述
代碼塊Block是蘋(píng)果在iOS4開(kāi)始引入的對(duì)C語(yǔ)言的擴(kuò)展,用來(lái)實(shí)現(xiàn)匿名函數(shù)的特性,Block是一種特殊的數(shù)據(jù)類型,其可以正常定義變量却特、作為參數(shù)堪藐、作為返回值,特殊地,Block還可以保存一段代碼,在需要的時(shí)候調(diào)用,目前Block已經(jīng)廣泛應(yīng)用于iOS開(kāi)發(fā)中,常用于GCD曾棕、動(dòng)畫(huà)获列、排序及各類回調(diào)
block 會(huì)在編譯過(guò)程中粱胜,會(huì)被當(dāng)做結(jié)構(gòu)體進(jìn)行處理烛占。 其結(jié)構(gòu)Block-ABI-Apple大概是這樣的:
struct Block_literal_1 {
void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor_1 {
unsigned long int reserved; // NULL
unsigned long int size; // sizeof(struct Block_literal_1)
// optional helper functions
void (*copy_helper)(void *dst, void *src); // IFF (1<<25)
void (*dispose_helper)(void *src); // IFF (1<<25)
// required ABI.2010.3.16
const char *signature; // IFF (1<<30)
} *descriptor;
// imported variables
};```
isa 指針會(huì)指向 block 所屬的類型,用于幫助運(yùn)行時(shí)系統(tǒng)進(jìn)行處理胁出。
Block 常見(jiàn)的類型有三種离唬,分別是` _NSConcreteStackBlock ` ` _NSConcreteMallocBlock` `_NSConcreteGlobalBlock` 。
另外還包括只在GC環(huán)境下使用的 `_NSConcreteFinalizingBlock` `_NSConcreteAutoBlock` ` _NSConcreteWeakBlockVariable`划鸽。
下面摘自 [libclosure-65 – Block_private.h-213](http://opensource.apple.com/source/libclosure/libclosure-65/runtime.c)
```objectivec
// the raw data space for runtime classes for blocks
// class+meta used for stack, malloc, and collectable based blocks
BLOCK_EXPORT void * _NSConcreteMallocBlock[32]
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
BLOCK_EXPORT void * _NSConcreteAutoBlock[32]
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
BLOCK_EXPORT void * _NSConcreteFinalizingBlock[32]
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
BLOCK_EXPORT void * _NSConcreteWeakBlockVariable[32]
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
// declared in Block.h
// BLOCK_EXPORT void * _NSConcreteGlobalBlock[32];
// BLOCK_EXPORT void * _NSConcreteStackBlock[32];```
##Block的幾種類型
1._NSConcreteGlobalBlock & _NSConcreteStackBlock
2._NSConcreteMallocBlock
3._NSConcreteFinalizingBlock & _NSConcreteAutoBlock
4._NSConcreteWeakBlockVariable
### _NSConcreteGlobalBlock & _NSConcreteStackBlock
`_NSConcreteGlobalBlock`& ` _NSConcreteStackBlock `是 block 初始化時(shí)設(shè)置的類型。
在以下情況中,block 會(huì)初始化為` _NSConcreteGlobalBlock`:
1.未捕獲外部變量裸诽。
在 [static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,CGBlockInfo &info)](http://clang.llvm.org/doxygen/CGBlocks_8cpp_source.html#l00326) 函數(shù)內(nèi)的 [334行](http://clang.llvm.org/doxygen/CGBlocks_8cpp_source.html#l00334) 至 [339行](http://clang.llvm.org/doxygen/CGBlocks_8cpp_source.html#l00339)嫂用,通過(guò)判斷 block(以及嵌套的block) 是否捕捉了本地存儲(chǔ)(原文為:local storage),未捕獲時(shí)丈冬,block 會(huì)初始化為`_NSConcreteGlobalBlock`
```objectivec
if (!block->hasCaptures()) {
info.StructureType =
llvm::StructType::get(CGM.getLLVMContext(), elementTypes, true);
info.CanBeGlobal = true;
return;
}
2.當(dāng)需要布局(layout)的變量的數(shù)量為0時(shí)嘱函。
在static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,CGBlockInfo &info)函數(shù)內(nèi),通過(guò)計(jì)算 block 的布局(layout)埂蕊。當(dāng)需要布局的變量為0時(shí)往弓,block 會(huì)初始化為_NSConcreteGlobalBlock
統(tǒng)計(jì)需要布局(layout)的變量:
-
this
(為了訪問(wèn)c++
的成員變量和函數(shù),需要this
指針) - 依次按下列規(guī)則處理捕獲的變量:
- 不需要計(jì)算布局的變量:
- 生命周期為靜態(tài)的變量(被
const
static
修飾的變量蓄氧,不被函數(shù)包含的靜態(tài)常量函似,c++中生命周期為靜態(tài)的變量) - 函數(shù)參數(shù)
- 生命周期為靜態(tài)的變量(被
- 需要計(jì)算布局的變量:被
__block
修飾的變量,以上未提到的類型(比如block)
Tips:當(dāng)需要布局(layout)的變量的統(tǒng)計(jì)完畢后喉童,會(huì)按照以下順序進(jìn)行一次穩(wěn)定排序撇寞。
- 不需要計(jì)算布局的變量:
- __strong 修飾的變量
- ByRef 類型
- __weak 修飾的變量
- 其它類型
_NSConcreteMallocBlock
在非垃圾收集環(huán)境下,當(dāng)_NSConcreteStackBlock
類型的block 被真正復(fù)制時(shí)堂氯,在_Block_copy_internal
方法內(nèi)部蔑担,會(huì)轉(zhuǎn)換為 _NSConcreteMallocBlock
libclosure-65/runtime.c
// Its a stack block. Make a copy.
if (!isGC) {
struct Block_layout *result = malloc(aBlock->descriptor->size);
if (!result) return NULL;
memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
// reset refcount
result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING); // XXX not needed
result->flags |= BLOCK_NEEDS_FREE | 2; // logical refcount 1
result->isa = _NSConcreteMallocBlock;
_Block_call_copy_helper(result, aBlock);
return result;
}```
###_NSConcreteFinalizingBlock & _NSConcreteAutoBlock
在垃圾收集環(huán)境下,當(dāng) block 被復(fù)制時(shí)咽白,如果block 有 ctors & dtors 時(shí)啤握,則會(huì)轉(zhuǎn)換為 `_NSConcreteFinalizingBlock`類型,反之晶框,則會(huì)轉(zhuǎn)換為`_NSConcreteAutoBlock` 類型
if (hasCTOR) {
result->isa = _NSConcreteFinalizingBlock;
}
else {
result->isa = _NSConcreteAutoBlock;
}```
_NSConcreteWeakBlockVariable
GC環(huán)境下排抬,當(dāng)對(duì)象被__weak
__block
修飾,且從棧復(fù)制到堆時(shí)三妈,block 會(huì)被標(biāo)記為 _NSConcreteWeakBlockVariable
類型畜埋。
bool isWeak = ((flags & (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK)) == (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK));
// if its weak ask for an object (only matters under GC)
struct Block_byref *copy = (struct Block_byref *)_Block_allocator(src->size, false, isWeak);
copy->flags = src->flags | _Byref_flag_initial_value; // non-GC one for caller, one for stack
copy->forwarding = copy; // patch heap copy to point to itself (skip write-barrier)
src->forwarding = copy; // patch stack to point to heap copy
copy->size = src->size;
if (isWeak) {
copy->isa = &_NSConcreteWeakBlockVariable; // mark isa field so it gets weak scanning
}```