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
}```
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容