深入研究Block实现原理

kublia 7年前
   <p>摘要</p>    <p>Blocks是C语言的扩充功能, iOS 4中引入了这个新功能“Blocks”,那么block到底是什么东西呢。其实它就是一个闭包,一个带有自动变量(局部变量)的匿名函数。很多语言也实现自己的闭包,比如C#的lamda表达式。这篇文章将从分析源码的角度来分析下block到底是什么鬼。</p>    <p>研究工具:clang</p>    <p>为了研究编译器的实现原理,我们使用clang(LLVM编译器,和GCC类似),通过命令 clang -rewrite-objc main.m ,解析main.m,这样我们就会得到对应的cpp文件main.cpp,就能看到block内部实现代码(后面有源码),借此可以研究 block 中各个特性的源码实现方式。</p>    <h2>一、 block捕获外部变量</h2>    <p style="text-align:center"><img src="https://simg.open-open.com/show/c021ab05234559a1e57fb147bfe87fce.png"></p>    <p>说到block怎么捕获外部变量,我们要知道c语言中的5种变量:</p>    <ul>     <li>自动变量</li>     <li>函数参数</li>     <li>静态变量</li>     <li>静态全局变量</li>     <li>全局变量</li>    </ul>    <p>今天主要对除函数参数变量之外的四种变量的捕获情况进行研究</p>    <p>根据这四种变量写出测试代码如下:</p>    <p><img src="https://simg.open-open.com/show/1ddaf66291c3eede0f753e39914c1f9d.png"></p>    <p>测试代码出错,原因是变量d没有加 __block 修饰,由于 __block 稍微复杂,我们后边再讲解,现在我们先对静态变量、静态全局变量、全局变量进行分析,代码:</p>    <pre>  <code class="language-cpp">#import <Foundation/Foundation.h>  int global_a = 1;  static int static_global_b = 2;  int main(int argc, const char * argv[]) {        static int static_c = 3;      int d = 4;      void(^TestBlock)() = ^{          NSLog(@"block内部:global_a = %d, static_global_b = %d, static_c = %d, d = %d", global_a, static_global_b, static_c, d);      };      global_a ++;      static_global_b ++;      static_c ++;      d ++;      NSLog(@"block外部:global_a = %d, static_global_b = %d, static_c = %d, d = %d", global_a, static_global_b, static_c, d);      TestBlock();        return 0;  }</code></pre>    <p>运行结果:</p>    <pre>  <code class="language-cpp">block外部:global_a = 2, static_global_b = 3, static_c = 4, d = 5  block内部:global_a = 2, static_global_b = 3, static_c = 4, d = 4</code></pre>    <p>在这里有两个问题:</p>    <p>1、为何不加__block就不能修改自动变量的值?</p>    <p>2、为何自动变量的值没有增加,其他变量的值增加?自动变量什么情况下才能在block中增加修改?</p>    <p>为了弄清楚以上两个疑问,我们用clang转换一下源码分析:</p>    <pre>  <code class="language-cpp">int global_a = 1;  static int static_global_b = 2;    struct __main_block_impl_0 {    struct __block_impl impl;    struct __main_block_desc_0* Desc;    int *static_c;    int d;    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_c, int _d, int flags=0) : static_c(_static_c), d(_d) {      impl.isa = &_NSConcreteStackBlock;      impl.Flags = flags;      impl.FuncPtr = fp;      Desc = desc;    }  };  static void __main_block_func_0(struct __main_block_impl_0 *__cself) {    int *static_c = __cself->static_c; // bound by copy    int d = __cself->d; // bound by copy            NSLog((NSString *)&__NSConstantStringImpl__var_folders_nb_9b7x604975q0vcgs2w0_h7xm0000gq_T_main_4acb3a_i_0, global_a, static_global_b, (*static_c), d);      }    static struct __main_block_desc_0 {    size_t reserved;    size_t Block_size;  } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};  int main(int argc, const char * argv[]) {        static int static_c = 3;      int d = 4;      void(*TestBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &static_c, d));      global_a ++;      static_global_b ++;      static_c ++;      d ++;      NSLog((NSString *)&__NSConstantStringImpl__var_folders_nb_9b7x604975q0vcgs2w0_h7xm0000gq_T_main_4acb3a_i_1, global_a, static_global_b, static_c, d);      ((void (*)(__block_impl *))((__block_impl *)TestBlock)->FuncPtr)((__block_impl *)TestBlock);        return 0;  }</code></pre>    <p>我们先简单解释下三个概念: __main_block_impl_0 、 __main_block_func_0 、 __main_block_desc_0</p>    <ul>     <li> <p>__main_block_func_0</p> <p>block内部的实现函数,其中__block_impl中的FuncPtr指向这个函数</p> </li>     <li> <p>__main_block_desc_0</p> <p>1、 reserved :保留字段默认为0</p> <p>2、 Block_size :为 sizeof(struct __main_block_impl_0) ,用来表示block所占内存大小。因为没有持有变量,block大小为impl的大小加上Desc指针大小</p> <p>3、 __main_block_desc_0_DATA : __main_block_desc_0 的一个结构体实例</p> <p>这个结构体,用来描述block的大小等信息。如果持有可修改的捕获变量时(即加__block),会增加两个函数(copy和dispose),我们后面会分析</p> </li>     <li> <p>main_block_impl_0</p> <p>我们可以看到</p> main_block_impl_0结构体中包含 __block_impl 、 __main_block_desc_0 两个类型的结构体变量,其中 __block_impl 的内部实现源码如下: <pre>  <code class="language-cpp">struct __block_impl {  void *isa;  int Flags;  int Reserved;  void *FuncPtr;  };</code></pre> 1、 isa指针 ,如果我们对runtime了解的话,就明白isa指向Class的指针。<br> 2、 Flags ,当block被copy时,应该执行的操作<br> 3、 Reserved 为保留字段<br> 4、 FuncPtr指针 ,指向block内的函数实现<br> __block_impl 保存block的类型isa(如&_NSConcreteStackBlock),标识(当block发生copy时,会用到),block的方法。<br> 接下来我们看下在main函数中block实现代码: <pre>  <code class="language-cpp">void(*TestBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &static_c, d));</code></pre> 去掉一些类型转换代码: <pre>  <code class="language-cpp">void(*TestBlock)() = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, &static_c, d));</code></pre> 调用block时的代码: <pre>  <code class="language-cpp">((void (*)(__block_impl *))((__block_impl *)TestBlock)->FuncPtr)((__block_impl *)TestBlock);</code></pre> 去掉类型转换之后代码: <pre>  <code class="language-cpp">TestBlock->FuncPtr(TestBlock);</code></pre> 可以看到以上代码是调用 __main_block_impl_0 结构体中的构造函数,将变量传入结构体内部保存,之后将这个结构体作为参数传给FuncPtr指向的函数即 __main_block_func_0 , 其中静态变量static_c传入block内部的是地址,自动变量传入的是值,而且在block外部执行 d++ 之前已经将d的值捕获进入block内部, 这也就能说明为何block内部不能改变静态变量的值的原因<br> 最终在block内部实现结果: <pre>  <code class="language-cpp">impl.isa = &_NSConcreteStackBlock;  impl.Flags = 0;  impl.FuncPtr = __main_block_func_0;  Desc = &__main_block_desc_0_DATA;  static_c = 3;  d = 4;</code></pre> </li>    </ul>    <p>到此,__main_block_impl_0结构体就是这样把自动变量捕获进来的。也就是说,在执行Block语法的时候,Block语法表达式所使用的自动变量的值是被保存进了Block的结构体实例中,也就是Block自身中。</p>    <p>这里值得说明的一点是,如果Block外面还有很多自动变量,静态变量,等等,这些变量在Block里面并不会被使用到。那么这些变量并不会被Block捕获进来,也就是说并不会在构造函数里面传入它们的值。</p>    <p>Block捕获外部变量仅仅只捕获Block闭包里面会用到的值,其他用不到的值,它并不会去捕获。</p>    <p>我们再来看一下__main_block_func_0函数</p>    <pre>  <code class="language-cpp">static void __main_block_func_0(struct __main_block_impl_0 *__cself) {    int *static_c = __cself->static_c; // bound by copy    int d = __cself->d; // bound by copy            NSLog((NSString *)&__NSConstantStringImpl__var_folders_nb_9b7x604975q0vcgs2w0_h7xm0000gq_T_main_4acb3a_i_0, global_a, static_global_b, (*static_c), d);     }</code></pre>    <p>可以看到,在函数内部通过 __cself->static_c 、 __cself->d 来获取 static_c 和 d 的值,由于结构体中捕获了变量的值,因此 __main_block_impl_0 类型 __cself 能够获取到内部保存的变量值,但是在函数内部只能修改捕获地址值的 static_c 变量,不能修改传入值变量的 d 的值。</p>    <p>到此为止,上面提出的二个问题就解开答案了。首先全局变量global_a和静态全局变量static_global_b的值增加,以及它们被Block捕获进去,这一点很好理解,因为是全局的,作用域很广,所以Block捕获了它们进去之后,在Block里面进行++操作,就像局部函数一样,可以成功修改全局变量的值,Block结束之后,它们的值依旧可以得以保存下来。自动变量是以值传递方式传递到Block的构造函数里面去的。Block只捕获Block中会用到的变量。由于只捕获了自动变量的值,并非内存地址,所以Block内部不能改变自动变量的值。Block捕获的外部变量可以改变值的是静态变量,静态全局变量,全局变量。</p>    <p>总结一下:在Block中改变变量值有2种方式,一是传递内存地址指针到Block中,二是改变存储区方式(__block)。</p>    <p>我们先试一下第一种方式:传递内存地址,代码:</p>    <pre>  <code class="language-cpp">#import <Foundation/Foundation.h>  int main(int argc, const char * argv[]) {        NSMutableString *str = [NSMutableString stringWithString:@"123"];      void(^TestBlock)() = ^{          [str appendString:@" 456"];          NSLog(@"block中 %@", str);      };      NSLog(@"block前 %@", str);      TestBlock();        return 0;  }</code></pre>    <p>控制台输出:</p>    <pre>  <code class="language-cpp">block前 123  block中 123 456</code></pre>    <p>内部实现源码:</p>    <pre>  <code class="language-cpp">struct __main_block_impl_0 {    struct __block_impl impl;    struct __main_block_desc_0* Desc;    NSMutableString *str;    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSMutableString *_str, int flags=0) : str(_str) {      impl.isa = &_NSConcreteStackBlock;      impl.Flags = flags;      impl.FuncPtr = fp;      Desc = desc;    }  };  static void __main_block_func_0(struct __main_block_impl_0 *__cself) {    NSMutableString *str = __cself->str; // bound by copy            ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)str, sel_registerName("appendString:"), (NSString *)&__NSConstantStringImpl__var_folders_nb_9b7x604975q0vcgs2w0_h7xm0000gq_T_main_fde5a9_mi_1);          NSLog((NSString *)&__NSConstantStringImpl__var_folders_nb_9b7x604975q0vcgs2w0_h7xm0000gq_T_main_fde5a9_mi_2, str);      }  static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->str, (void*)src->str, 3/*BLOCK_FIELD_IS_OBJECT*/);}    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->str, 3/*BLOCK_FIELD_IS_OBJECT*/);}    static struct __main_block_desc_0 {    size_t reserved;    size_t Block_size;    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);    void (*dispose)(struct __main_block_impl_0*);  } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};  int main(int argc, const char * argv[]) {        NSMutableString *str = ((NSMutableString *(*)(id, SEL, NSString *))(void *)objc_msgSend)((id)objc_getClass("NSMutableString"), sel_registerName("stringWithString:"), (NSString *)&__NSConstantStringImpl__var_folders_nb_9b7x604975q0vcgs2w0_h7xm0000gq_T_main_fde5a9_mi_0);      void(*TestBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, str, 570425344));      NSLog((NSString *)&__NSConstantStringImpl__var_folders_nb_9b7x604975q0vcgs2w0_h7xm0000gq_T_main_fde5a9_mi_3, str);      ((void (*)(__block_impl *))((__block_impl *)TestBlock)->FuncPtr)((__block_impl *)TestBlock);        return 0;  }</code></pre>    <p>__main_block_impl_0构造函数</p>    <pre>  <code class="language-cpp">__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSMutableString *_str, int flags=0) : str</code></pre>    <p>可以看出传递的是NSMutableString *类型,即传递的地址,进而可以改变str的值。</p>    <p>上边代码中我们可以看到 __main_block_copy_0 和_ _main_block_dispose_0 概念,接下来对这两个概念进行初步讲解.</p>    <h2>二、Block的copy和dispose</h2>    <p style="text-align:center"><img src="https://simg.open-open.com/show/bf0e1452830d007079ea97e81e381aa1.png"></p>    <p>OC中,一般Block就分为以下3种,_NSConcreteStackBlock,_NSConcreteMallocBlock,_NSConcreteGlobalBlock</p>    <p>先来说明一下3者的区别。</p>    <p>1.从捕获外部变量的角度上来看</p>    <ul>     <li> <p>_NSConcreteStackBlock:</p> <p>只用到外部局部变量、成员属性变量,且没有强指针引用的block都是StackBlock。</p> <p>StackBlock的生命周期由系统控制的,一旦返回之后,就被系统销毁了。</p> </li>     <li> <p>_NSConcreteMallocBlock:</p> <p>有强指针引用或copy修饰的成员属性引用的block会被复制一份到堆中成为MallocBlock,没有强指针引用即销毁,生命周期由程序员控制</p> </li>     <li> <p>_NSConcreteGlobalBlock:</p> <p>没有用到外界变量或只用到全局变量、静态变量的block为_NSConcreteGlobalBlock,生命周期从创建到应用程序结束。</p> </li>    </ul>    <p>没有用到外部变量肯定是_NSConcreteGlobalBlock,这点很好理解。不过只用到全局变量、静态变量的block也是_NSConcreteGlobalBlock。举例如下:</p>    <pre>  <code class="language-cpp">#import <Foundation/Foundation.h>  int global_a = 1;  static int static_global_b = 2;    int main(int argc, const char * argv[]) {        static int static_c = 3;      void (^myBlock)(void) = ^{          NSLog(@"Block中 变量 = %d %d %d",static_global_b ,static_c, global_a);      };        NSLog(@"%@",myBlock);      myBlock();        return 0;  }</code></pre>    <p>控制台结果:</p>    <pre>  <code class="language-cpp"><__NSMallocBlock__: 0x100203980>  Block中 变量 = 2 3 1</code></pre>    <p>可见,只用到全局变量、静态变量的block也可以是_NSConcreteGlobalBlock。</p>    <p>所以在ARC环境下,3种类型都可以捕获外部变量。</p>    <p>2. 从持有对象的角度上来看:</p>    <ul>     <li> <p>_NSConcreteStackBlock是不持有对象的</p> <pre>  <code class="language-cpp">#import <Foundation/Foundation.h>  int main(int argc, const char * argv[]) {      NSObject *obj = [[NSObject alloc]init];    void (^myBlock)(void) = ^{         NSLog(@"block中 %ld",obj.retainCount);    };      NSLog(@"block外 %ld",obj.retainCount);    myBlock();      return 0;  }</code></pre> <p>输出结果:</p> <pre>  <code class="language-cpp">block外 1  block中 1</code></pre> </li>     <li> <p>_NSConcreteMallocBlock是持有对象的</p> <pre>  <code class="language-cpp">//以下是在MRC下执行的  #import <Foundation/Foundation.h>  int main(int argc, const char * argv[]) {      NSObject *obj = [[NSObject alloc]init];    NSLog(@"block-1 %ld",obj.retainCount);    void (^myBlock)(void) = [^{         NSLog(@"block-3 %ld",obj.retainCount);    } copy];      NSLog(@"block-2 %ld",obj.retainCount);    myBlock();      return 0;  }</code></pre> <p>输出结果:</p> <pre>  <code class="language-cpp">block-1 1  block-1 2  block-1 2</code></pre> </li>     <li> <p>_NSConcreteGlobalBlock也不持有对象</p> <pre>  <code class="language-cpp">#import <Foundation/Foundation.h>  int main(int argc, const char * argv[]) {      void (^myBlock)(void) = ^{         NSObject *obj = [[NSObject alloc]init];         NSLog(@"block %ld",obj.retainCount);    };    myBlock();      return 0;  }</code></pre> <p>输出结果:</p> <pre>  <code class="language-cpp">block 1</code></pre> <p>由于_NSConcreteStackBlock所属的变量域一旦结束,那么该Block就会被销毁。在ARC环境下,编译器会自动的判断,把Block自动的从栈copy到堆。比如当Block作为函数返回值的时候,肯定会copy到堆上。</p> </li>     <li>手动调用copy</li>     <li>Block是函数的返回值</li>     <li>Block被强引用,Block被赋值给__strong或者id类型</li>     <li>调用系统API入参中含有usingBlcok的方法</li>    </ul>    <p>以上4种情况,系统都会默认调用copy方法把Block赋复制</p>    <p>但是当Block为函数参数的时候,就需要我们手动的copy一份到堆上了。这里除去系统的API我们不需要管,比如GCD等方法中本身带usingBlock的方法,其他我们自定义的方法传递Block为参数的时候都需要手动copy一份到堆上。</p>    <p>copy函数把Block从栈上拷贝到堆上,dispose函数是把堆上的函数在废弃的时候销毁掉。</p>    <pre>  <code class="language-cpp">#define Block_copy(...) ((__typeof(__VA_ARGS__))_Block_copy((const void *)(__VA_ARGS__)))  #define Block_release(...) _Block_release((const void *)(__VA_ARGS__))    // Create a heap based copy of a Block or simply add a reference to an existing one.  // This must be paired with Block_release to recover memory, even when running  // under Objective-C Garbage Collection.  BLOCK_EXPORT void *_Block_copy(const void *aBlock)      __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);    // Lose the reference, and if heap based and last reference, recover the memory  BLOCK_EXPORT void _Block_release(const void *aBlock)      __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);    // Used by the compiler. Do not call this function yourself.  BLOCK_EXPORT void _Block_object_assign(void *, const void *, const int)      __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);    // Used by the compiler. Do not call this function yourself.  BLOCK_EXPORT void _Block_object_dispose(const void *, const int)      __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);</code></pre>    <p>上面是源码中2个常用的宏定义和4个常用的方法,一会我们就会看到这4个方法。</p>    <pre>  <code class="language-cpp">static void *_Block_copy_internal(const void *arg, const int flags) {      struct Block_layout *aBlock;      const bool wantsOne = (WANTS_ONE & flags) == WANTS_ONE;      if (!arg) return NULL;     aBlock = (struct Block_layout *)arg;     if (aBlock->flags & BLOCK_NEEDS_FREE) {       latching_incr_int(&aBlock->flags);         return aBlock;       } else if (aBlock->flags & BLOCK_IS_GLOBAL) {         return aBlock;       }      struct Block_layout *result = malloc(aBlock->descriptor->size);     if (!result) return (void *)0;     memmove(result, aBlock, aBlock->descriptor->size);     result->flags &= ~(BLOCK_REFCOUNT_MASK);    result->flags |= BLOCK_NEEDS_FREE | 1;      result->isa = _NSConcreteMallocBlock;      if (result->flags & BLOCK_HAS_COPY_DISPOSE) {       (*aBlock->descriptor->copy)(result, aBlock);      }     return result;  }</code></pre>    <p>上面这一段是Block_copy的一个实现,实现了从_NSConcreteStackBlock复制到_NSConcreteMallocBlock的过程.</p>    <pre>  <code class="language-cpp">void _Block_release(void *arg) {      struct Block_layout *aBlock = (struct Block_layout *)arg;      if (!aBlock) return;      int32_t newCount;      newCount = latching_decr_int(&aBlock->flags) & BLOCK_REFCOUNT_MASK;      if (newCount > 0) return;      if (aBlock->flags & BLOCK_NEEDS_FREE) {          if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE)(*aBlock->descriptor->dispose)(aBlock);          _Block_deallocator(aBlock);      }      else if (aBlock->flags & BLOCK_IS_GLOBAL) {          ;      }      else {          printf("Block_release called upon a stack Block: %p, ignored\\\\n", (void *)aBlock);      }  }</code></pre>    <p>上面这一段是Block_release的一个实现,实现了怎么释放一个Block。</p>    <p>因为在C语言的结构体中,编译器没法很好的进行初始化和销毁操作。这样对内存管理来说是很不方便的。所以就在 __main_block_desc_0 结构体中间增加成员变量 void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*) 和 void (*dispose)(struct __main_block_impl_0*) ,利用OC的Runtime进行内存管理。</p>    <p>相应的增加了2个方法。</p>    <pre>  <code class="language-cpp">static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->str, (void*)src->str, 3/*BLOCK_FIELD_IS_OBJECT*/);}    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->str, 3/*BLOCK_FIELD_IS_OBJECT*/);}</code></pre>    <p>这里的 _Block_object_assign 和 _Block_object_dispose 就对应着retain和release方法。</p>    <h2>三.Block中__block实现原理</h2>    <p style="text-align:center"><img src="https://simg.open-open.com/show/b1bcdefb58c73bafb3d7a7fc4efdfe65.png"></p>    <p>1.普通非对象的变量</p>    <p>先来看看普通变量的情况</p>    <pre>  <code class="language-cpp">#import <Foundation/Foundation.h>  int main(int argc, const char * argv[]) {        __block int a = 0;      void (^myBlock)(void) = ^{          a ++;      };      myBlock();        return 0;  }</code></pre>    <p>转化后的代码:</p>    <pre>  <code class="language-cpp">struct __Block_byref_a_0 {    void *__isa;  __Block_byref_a_0 *__forwarding;   int __flags;   int __size;   int a;  };    struct __main_block_impl_0 {    struct __block_impl impl;    struct __main_block_desc_0* Desc;    __Block_byref_a_0 *a; // by ref    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {      impl.isa = &_NSConcreteStackBlock;      impl.Flags = flags;      impl.FuncPtr = fp;      Desc = desc;    }  };  static void __main_block_func_0(struct __main_block_impl_0 *__cself) {    __Block_byref_a_0 *a = __cself->a; // bound by ref            (a->__forwarding->a) ++;      }  static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}    static struct __main_block_desc_0 {    size_t reserved;    size_t Block_size;    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);    void (*dispose)(struct __main_block_impl_0*);  } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};  int main(int argc, const char * argv[]) {        __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 0};      void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));      ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);        return 0;  }</code></pre>    <p>从以上代码看出</p>    <ul>     <li>被__block修饰的变量,内部实现多了一个 __Block_byref_a_0 类型的结构体,这个结构体有5个成员变量。第一个是 isa指针 ,第二个是指向自身类型的 __forwarding 指针,第三个是一个标记 flag ,第四个是它的大小,第五个是变量值。</li>     <li>被__block修饰的变量a转化成 __Block_byref_a_0 类型的变量a</li>     <li>将 __Block_byref_a_0 类型的变量a的地址传入 __main_block_impl_0 内部的 __Block_byref_a_0 修饰的变量a</li>     <li>在 __main_block_func_0 函数中通过 __cself->a 取到 __Block_byref_a_0 结构体变量,在通过 (a->__forwarding->a) ++ 实现被__block修饰的变量a的值加一</li>    </ul>    <p>ARC环境下,一旦Block赋值就会触发copy, <strong>block就会copy到堆上,Block也是</strong> NSMallocBlock。ARC环境下也是存在 <strong>NSStackBlock的时候,这种情况下,</strong> block就在栈上。</p>    <p>MRC环境下,只有copy, <strong>block才会被复制到堆上,否则,</strong> block一直都在栈上,block也只是 <strong>NSStackBlock,这个时候</strong> forwarding指针就只指向自己了。</p>    <p>2.对象的变量</p>    <pre>  <code class="language-cpp">//ARC环境下  #import <Foundation/Foundation.h>  int main(int argc, const char * argv[]) {        __block NSObject *block_obj = [[NSObject alloc] init];      NSObject *obj = [[NSObject alloc] init];      NSLog(@"block外 block_obj:%p;  obj:%p", █_obj, &obj);      void (^myBlock)(void) = ^{          NSLog(@"block中 block_obj:%p;  obj:%p", █_obj, &obj);      };      myBlock();      NSLog(@"%@", myBlock);      return 0;  }</code></pre>    <p>结果输出:</p>    <pre>  <code class="language-cpp">block外 block_obj:0x7fff5fbff758;  obj:0x7fff5fbff728  block中 block_obj:0x100300578;  obj:0x1003001e0  <__NSMallocBlock__: 0x1002004d0></code></pre>    <p>以上代码转换后:</p>    <pre>  <code class="language-cpp">struct __Block_byref_block_obj_0 {    void *__isa;  __Block_byref_block_obj_0 *__forwarding;   int __flags;   int __size;   void (*__Block_byref_id_object_copy)(void*, void*);   void (*__Block_byref_id_object_dispose)(void*);   NSObject *block_obj;  };    struct __main_block_impl_0 {    struct __block_impl impl;    struct __main_block_desc_0* Desc;    NSObject *obj;    __Block_byref_block_obj_0 *block_obj; // by ref    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *_obj, __Block_byref_block_obj_0 *_block_obj, int flags=0) : obj(_obj), block_obj(_block_obj->__forwarding) {      impl.isa = &_NSConcreteStackBlock;      impl.Flags = flags;      impl.FuncPtr = fp;      Desc = desc;    }  };  static void __main_block_func_0(struct __main_block_impl_0 *__cself) {    __Block_byref_block_obj_0 *block_obj = __cself->block_obj; // bound by ref    NSObject *obj = __cself->obj; // bound by copy            NSLog((NSString *)&__NSConstantStringImpl__var_folders_nb_9b7x604975q0vcgs2w0_h7xm0000gq_T_main_0486e2_mi_1, &(block_obj->__forwarding->block_obj), &obj);      }  static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->block_obj, (void*)src->block_obj, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&dst->obj, (void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);}    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->block_obj, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);}    static struct __main_block_desc_0 {    size_t reserved;    size_t Block_size;    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);    void (*dispose)(struct __main_block_impl_0*);  } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};  int main(int argc, const char * argv[]) {        __attribute__((__blocks__(byref))) __Block_byref_block_obj_0 block_obj = {(void*)0,(__Block_byref_block_obj_0 *)█_obj, 33554432, sizeof(__Block_byref_block_obj_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"))};      NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));      NSLog((NSString *)&__NSConstantStringImpl__var_folders_nb_9b7x604975q0vcgs2w0_h7xm0000gq_T_main_0486e2_mi_0, &(block_obj.__forwarding->block_obj), &obj);      void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, obj, (__Block_byref_block_obj_0 *)█_obj, 570425344));      ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);        return 0;  }</code></pre>    <p>首先需要说明的一点是对象在OC中,默认声明自带__strong所有权修饰符的,所以main开头我们声明的</p>    <pre>  <code class="language-cpp">__block  NSObject *block_obj = [[NSObject alloc] init];  NSObject *obj = [[NSObject alloc] init];</code></pre>    <p>等价于:</p>    <pre>  <code class="language-cpp">__block __strong  NSObject *block_obj = [[NSObject alloc] init];  __strong NSObject *obj = [[NSObject alloc] init];</code></pre>    <p>从以上转化代码看出:</p>    <ul>     <li>被__block 修饰的变量block_obj转化成 __Block_byref_block_obj_0 的变量,名称相同</li>     <li>__main_block_impl_0 构造方法中将 __Block_byref_block_obj_0 类型的 block_obj 变量的地址传入给结构体的 __Block_byref_block_obj_0 类型 block_obj`变量,将变量obj的地址传入给结构体的变量obj</li>     <li>在 __main_block_func_0 函数中通过 __cself->block_obj 获取 __Block_byref_block_obj_0 类型变量 block_obj ,再通过 block_obj->__forwarding->block_obj 获取外部捕获的变量;通过 __cself->obj 获取obj变量</li>     <li>Block捕获了 <strong>block,并且强引用了,因为在</strong> Block_byref_block_obj_0结构体中,有一个变量是id block_obj,这个默认也是带__strong所有权修饰符的</li>     <li>ARC环境下,Block捕获外部对象变量,是都会copy一份的,地址都不同。只不过带有__block修饰符的变量会被捕获到Block内部持有</li>    </ul>    <p>我们再来看看MRC环境下的情况,还是将上述代码的例子运行在MRC中:</p>    <pre>  <code class="language-cpp">block外 block_obj:0x7fff5fbff758;  obj:0x7fff5fbff728  block中 block_obj:0x7fff5fbff758;  obj:0x7fff5fbff700  <__NSStackBlock__: 0x7fff5fbff6e0></code></pre>    <p>这个时候block在栈上, <strong>NSStackBlock</strong> ,可以打印出来retainCount值都是1。当把这个block copy一下,就变成 <strong>NSMallocBlock</strong> ,对象的retainCount值就会变成2了。</p>    <h3>总结:</h3>    <p>在MRC环境下,</p>    <p>block根本不会对指针所指向的对象执行copy操作,而只是把指针进行的复制。</p>    <p>而在ARC环境下,对于声明为</p>    <p>block的外部对象,在block内部会进行retain,以至于在block环境内能安全的引用外部对象,所以才会产生循环引用的问题!</p>    <ul>     <li>对于非对象的变量<br> 自动变量的值,被copy进了Block,不带 <strong>block的自动变量只能在里面被访问,并不能改变值;带</strong> block的自动变量 和 静态变量 就是直接地址访问。所以在Block里面可以直接改变变量的值</li>     <li>静态全局变量,全局变量,函数参数<br> 可以在直接在Block中改变变量值的,但是他们并没有变成Block结构体 __main_block_impl_0 的成员变量,因为他们的作用域大,所以可以直接更改他们的值<br> 值得注意的是,静态全局变量,全局变量,函数参数他们并不会被Block持有,也就是说不会增加retainCount值</li>     <li>对于对象<br> 在MRC环境下, <p>block根本不会对指针所指向的对象执行copy操作,而只是把指针进行的复制。</p> <p>而在ARC环境下,对于声明为</p> block的外部对象,在block内部会进行retain,以至于在block环境内能安全的引用外部对象。</li>    </ul>    <h3>最后</h3>    <p>在ARC环境下,Block也是存在 __NSStackBlock 的时候的,平时见到最多的是 _NSConcreteMallocBlock ,是因为我们会对Block有赋值操作,所以ARC下,block 类型通过=进行传递时,会导致调用 objc_retainBlock->_Block_copy->_Block_copy_internal 方法链。并导致 __NSStackBlock__ 类型的 block 转换为 __NSMallocBlock__ 类型。</p>    <pre>  <code class="language-cpp">#import <Foundation/Foundation.h>  int main(int argc, const char * argv[]) {        __block int temp = 10;      NSLog(@"%@",^{NSLog(@"*******%d %p",temp ++,&temp);});      return 0;  }</code></pre>    <pre>  <code class="language-cpp"><__NSStackBlock__: 0x7fff5fbff768></code></pre>    <h3>参考:</h3>    <p><a href="/misc/goto?guid=4959730853746028998" rel="nofollow,noindex">http://www.jianshu.com/p/ca6ac0ae93ad</a></p>    <p><a href="/misc/goto?guid=4959730853850214545" rel="nofollow,noindex">http://www.jianshu.com/p/ee9756f3d5f6</a></p>    <p> </p>    <p>来自:http://www.jianshu.com/p/710026d5bcfb</p>    <p> </p>