iOS - Block类型简介

概述

代码块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
}```

你可能感兴趣的:(iOS - Block类型简介)