概述
代码块Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,Block是一种特殊的数据类型,其可以正常定义变量、作为参数、作为返回值,特殊地,Block还可以保存一段代码,在需要的时候调用,目前Block已经广泛应用于iOS开发中,常用于GCD、动画、排序及各类回调
block 会在编译过程中,会被当做结构体进行处理。 其结构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 指针会指向 block 所属的类型,用于帮助运行时系统进行处理。
Block 常见的类型有三种,分别是` _NSConcreteStackBlock ` ` _NSConcreteMallocBlock` `_NSConcreteGlobalBlock` 。
另外还包括只在GC环境下使用的 `_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 初始化时设置的类型。
在以下情况中,block 会初始化为` _NSConcreteGlobalBlock`:
1.未捕获外部变量。
在 [static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,CGBlockInfo &info)](http://clang.llvm.org/doxygen/CGBlocks_8cpp_source.html#l00326) 函数内的 [334行](http://clang.llvm.org/doxygen/CGBlocks_8cpp_source.html#l00334) 至 [339行](http://clang.llvm.org/doxygen/CGBlocks_8cpp_source.html#l00339),通过判断 block(以及嵌套的block) 是否捕捉了本地存储(原文为:local storage),未捕获时,block 会初始化为`_NSConcreteGlobalBlock`
```objectivec
if (!block->hasCaptures()) {
info.StructureType =
llvm::StructType::get(CGM.getLLVMContext(), elementTypes, true);
info.CanBeGlobal = true;
return;
}
2.当需要布局(layout)的变量的数量为0时。
在static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,CGBlockInfo &info)函数内,通过计算 block 的布局(layout)。当需要布局的变量为0时,block 会初始化为_NSConcreteGlobalBlock
统计需要布局(layout)的变量:
-
this
(为了访问c++
的成员变量和函数,需要this
指针) - 依次按下列规则处理捕获的变量:
- 不需要计算布局的变量:
- 生命周期为静态的变量(被
const
static
修饰的变量,不被函数包含的静态常量,c++中生命周期为静态的变量) - 函数参数
- 生命周期为静态的变量(被
- 需要计算布局的变量:被
__block
修饰的变量,以上未提到的类型(比如block)
Tips:当需要布局(layout)的变量的统计完毕后,会按照以下顺序进行一次稳定排序。
- 不需要计算布局的变量:
- __strong 修饰的变量
- ByRef 类型
- __weak 修饰的变量
- 其它类型
_NSConcreteMallocBlock
在非垃圾收集环境下,当_NSConcreteStackBlock
类型的block 被真正复制时,在_Block_copy_internal
方法内部,会转换为 _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
在垃圾收集环境下,当 block 被复制时,如果block 有 ctors & dtors 时,则会转换为 `_NSConcreteFinalizingBlock`类型,反之,则会转换为`_NSConcreteAutoBlock` 类型
if (hasCTOR) {
result->isa = _NSConcreteFinalizingBlock;
}
else {
result->isa = _NSConcreteAutoBlock;
}```
_NSConcreteWeakBlockVariable
GC环境下,当对象被__weak
__block
修饰,且从栈复制到堆时,block 会被标记为 _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
}```
网友评论