block(代码块)的介绍以及使用方法和变量之间的关系

来源:互联网 发布:音序器软件 编辑:程序博客网 时间:2024/06/06 00:07

目录(?)[+]

  • Block是什么?
    • Block的功能
  • Block的用法
  • 如何声明和定义Block
    • 直接使用Block
    • Block和Cocoa
  • block和变量之间的关系
    • 关于 __block 的进一步讨论
    • __block 和Object C对象之间的关系
    • __block 和C++对象之间的关系

Block是什么?

Block是C语言的一个语法特性,同时也是C语言的运行时特性,它很像C中的函数指针,因为你可以像使用函数指针一样的去使用block对象;它也很像C++中的函数对象,因为除了要执行的代码,block还可以携带和block绑定的状态信息。

因此,block是一个对象,这个对象里包含了要执行的代码片段以及一些状态信息。

MacOSX 10.6和iOS 4.0以上版本的Xcode开发包提供了对block的支持。

Block的功能

block是一片具有以下特性的内联代码片段集合:

  • 可以像函数一样有类型参数;
  • 可以声明或推算出一个返回类型;
  • 可以访问和block定义在同一个词法范围里的变量(即Status);
  • 可以修改同一个词法范围里的变量;
  • 同一个词法范围的block之间可以共享变量和变量的修改结果;
  • 当栈被摧毁后,栈里的block依旧可以保持状态信息;

Block的用法

作为一个自包含的代码片段,由于以下特性,block很适合作为回调函数的替代方案:

  • 你可以在方法的下上文中,调用block的地方直接编写构成block的代码片段;
  • block可以访问局部变量;

如何声明和定义Block

你可以通过^操作符定义一个block类型的变量,用{}来圈定block的代码片段,如下图所示:

再次声明:block可以访问和block定义在同一个词法范围里的变量。

<span class="kwd">int</span><span class="pln"> multiplier </span><span class="pun">=</span><span class="pln"> </span><span class="lit">7</span><span class="pun">;</span><span class="pln"></span><span class="kwd">int</span><span class="pln"> </span><span class="pun">(^</span><span class="pln">myBlock</span><span class="pun">)(</span><span class="kwd">int</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">^(</span><span class="kwd">int</span><span class="pln"> num</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> num </span><span class="pun">*</span><span class="pln"> multipiler</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln">printf</span><span class="pun">(</span><span class="str">"%d"</span><span class="pun">,</span><span class="pln"> myBlock</span><span class="pun">(</span><span class="lit">3</span><span class="pun">));</span><span class="pln"> </span><span class="com">// prints "21"</span>

直接使用Block

在更多的时候,你并不需要定义自己的Block类型,而是在API中直接编写block代码片段,例如:qsort_b。

<span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">myCharacter</span><span class="pun">[</span><span class="lit">3</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="str">"safari"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"ie"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"chrome"</span><span class="pln"> </span><span class="pun">};</span><span class="pln">qsort_b</span><span class="pun">(</span><span class="pln">myCharacter</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">sizeof</span><span class="pun">(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*),</span><span class="pln"> </span><span class="pun">^(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">l</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">r</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">    </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">left </span><span class="pun">=</span><span class="pln"> </span><span class="pun">*(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">**)</span><span class="pln">l</span><span class="pun">;</span><span class="pln">    </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">*</span><span class="pln">right </span><span class="pun">=</span><span class="pln"> </span><span class="pun">*(</span><span class="kwd">char</span><span class="pln"> </span><span class="pun">**)</span><span class="pln">r</span><span class="pun">;</span><span class="pln">    </span><span class="kwd">return</span><span class="pln"> strncmp</span><span class="pun">(</span><span class="pln">left</span><span class="pun">,</span><span class="pln"> right</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln"></span><span class="pun">});</span>

Block和Cocoa

Cocoa framework中很多方法使用了Block作为其参数(尽管也有对应的callback版本,但还是推荐使用 block版本)。在动画以及集合遍历方面,block很常见。

<span class="typ">NSArray</span><span class="pln"> </span><span class="pun">*</span><span class="pln">stringsArray </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="typ">NSArray</span><span class="pln"> arrayWithObjects</span><span class="pun">:</span><span class="pln">                                 </span><span class="pun">@</span><span class="str">"string 1"</span><span class="pun">,</span><span class="pln">                                 </span><span class="pun">@</span><span class="str">"String 21"</span><span class="pun">,</span><span class="pln">                                 </span><span class="pun">@</span><span class="str">"string 12"</span><span class="pun">,</span><span class="pln">                                 </span><span class="pun">@</span><span class="str">"String 11"</span><span class="pun">,</span><span class="pln">                                 </span><span class="pun">@</span><span class="str">"String 02"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">nil</span><span class="pun">];</span><span class="pln"></span><span class="kwd">static</span><span class="pln"> </span><span class="typ">NSStringCompareOptions</span><span class="pln"> comparisonOptions </span><span class="pun">=</span><span class="pln"> </span><span class="typ">NSCaseInsensitiveSearch</span><span class="pln"> </span><span class="pun">|</span><span class="pln">     </span><span class="typ">NSNumericSearch</span><span class="pln"> </span><span class="pun">|</span><span class="typ">NSWidthInsensitiveSearch</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="typ">NSForcedOrderingSearch</span><span class="pun">;</span><span class="pln"></span><span class="typ">NSLocale</span><span class="pln"> </span><span class="pun">*</span><span class="pln">currentLocale </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="typ">NSLocale</span><span class="pln"> currentLocale</span><span class="pun">];</span><span class="pln"></span><span class="typ">NSComparator</span><span class="pln"> finderSortBlock </span><span class="pun">=</span><span class="pln"> </span><span class="pun">^(</span><span class="pln">id string1</span><span class="pun">,</span><span class="pln"> id string2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">    </span><span class="typ">NSRange</span><span class="pln"> string1Range </span><span class="pun">=</span><span class="pln"> </span><span class="typ">NSMakeRange</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="pln">string1 length</span><span class="pun">]);</span><span class="pln">    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">[</span><span class="pln">string1 compare</span><span class="pun">:</span><span class="pln">string2                     options</span><span class="pun">:</span><span class="pln">comparisonOptions                       range</span><span class="pun">:</span><span class="pln">string1Range                     locale</span><span class="pun">:</span><span class="pln">currentLocale</span><span class="pun">];</span><span class="pln"></span><span class="pun">};</span><span class="pln"></span><span class="typ">NSArray</span><span class="pln"> </span><span class="pun">*</span><span class="pln">finderSortArray </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">stringsArray     sortedArrayUsingComparator</span><span class="pun">:</span><span class="pln">finderSortBlock</span><span class="pun">];</span><span class="pln"></span><span class="typ">NSLog</span><span class="pun">(@</span><span class="str">"finderSortArray: %@"</span><span class="pun">,</span><span class="pln"> finderSortArray</span><span class="pun">);</span>

block和变量之间的关系

这一部分涉及内存管理相关的内容,为了正确的使用block,理解并记住它们,很重要:

在一个block代码片段的内部,你可以使用三种不同类型的变量(就像你在函数里一样):

  • 全局变量(包括static locals);
  • 全局函数(尽管这并不是变量=。=);
  • 在包含block的词法范围内的局部变量;

当在一个Block里使用变量时,应遵循以下规则:

  • 在包含block的词法范围里的栈局部变量,在block内部是常量,只能只读访问。这些局部变量的值即block执行时,局部变量的值。在多层内嵌的block中,

局部变量的值,取最内层词法范围里,局部变量的值;

  • 被声明为__block存储类型的局部变量通过引用传递给block,因此是mutable的;在__block类型的局部变量的有效范围内,对该局部变量的修改,会在该有效范围内的所有block范围内生效;
  • 定义在block内部的局部变量,和函数内部的局部变量法则相同;

正确的:

<span class="kwd">int</span><span class="pln"> x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">123</span><span class="pun">;</span><span class="pln"></span><span class="kwd">void</span><span class="pln"> </span><span class="pun">(^</span><span class="pln">printXandY</span><span class="pun">)(</span><span class="kwd">int</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">^(</span><span class="kwd">int</span><span class="pln"> y</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">    printf</span><span class="pun">(</span><span class="str">"%d %d\n"</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">);</span><span class="pln"></span><span class="pun">};</span><span class="pln">printXandY</span><span class="pun">(</span><span class="lit">456</span><span class="pun">);</span>

错误的:

<span class="kwd">int</span><span class="pln"> x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">123</span><span class="pun">;</span><span class="pln"></span><span class="kwd">void</span><span class="pln"> </span><span class="pun">(^</span><span class="pln">printXandY</span><span class="pun">)(</span><span class="kwd">int</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">^(</span><span class="kwd">int</span><span class="pln"> y</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">    x </span><span class="pun">=</span><span class="pln"> x </span><span class="pun">+</span><span class="pln"> y</span><span class="pun">;</span><span class="pln"> </span><span class="com">// ERROR HERE!!! x should be __block</span><span class="pln">    printf</span><span class="pun">(</span><span class="str">"%d %d\n"</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">);</span><span class="pln"></span><span class="pun">};</span><span class="pln">printXandY</span><span class="pun">(</span><span class="lit">456</span><span class="pun">);</span>

关于__block的进一步讨论

  1. __block是只针对局部变量生效的一种描述变量存储类型的关键字,因此__block类型的变量都是栈变量;
  2. __block类型的变量在其定义的语法范围里,和该范围内的所有block共享存储空间,当block在被复制到heap区域时,同区域内的__block变量占用的内存不会随着退栈而销毁;
  3. 出于优化的考虑,栈中的block对象最开始和一般的栈局部变量是相同的,当使用Block_copy对block进行复制时,才被拷贝到heap区域;
  4. __block变量不能是一个可变长数组;

下面这个例子用于展示,各种类型的变量与__block之间的交互:

<span class="kwd">extern</span><span class="pln"> </span><span class="typ">NSInteger</span><span class="pln"> </span><span class="typ">CounterGlobal</span><span class="pun">;</span><span class="pln"></span><span class="kwd">static</span><span class="pln"> </span><span class="typ">NSInteger</span><span class="pln"> </span><span class="typ">CounterStatic</span><span class="pun">;</span><span class="pln"></span><span class="pun">{</span><span class="pln">    </span><span class="typ">NSInteger</span><span class="pln"> localCounter </span><span class="pun">=</span><span class="pln"> </span><span class="lit">42</span><span class="pun">;</span><span class="pln">    __block </span><span class="kwd">char</span><span class="pln"> localCharacter</span><span class="pun">;</span><span class="pln">    </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">(^</span><span class="pln">aBlock</span><span class="pun">)(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">^(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">        </span><span class="pun">++</span><span class="typ">CounterGlobal</span><span class="pun">;</span><span class="pln">        </span><span class="pun">++</span><span class="typ">CounterStatic</span><span class="pun">;</span><span class="pln">        </span><span class="typ">CounterGlobal</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> localCounter</span><span class="pun">;</span><span class="pln"> </span><span class="com">// localCounter fixed at block creation</span><span class="pln">        localCharacter </span><span class="pun">=</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">;</span><span class="pln"> </span><span class="com">// sets localCharacter in enclosing scope</span><span class="pln">    </span><span class="pun">};</span><span class="pln">    </span><span class="pun">++</span><span class="pln">localCounter</span><span class="pun">;</span><span class="pln"> </span><span class="com">// unseen by the block</span><span class="pln">    localCharacter </span><span class="pun">=</span><span class="pln"> </span><span class="str">'b'</span><span class="pun">;</span><span class="pln">    aBlock</span><span class="pun">();</span><span class="pln"> </span><span class="com">// execute the block</span><span class="pln">    </span><span class="com">// localCharacter now 'a'</span><span class="pln"></span><span class="pun">}</span>

__block和Object C对象之间的关系

如果,你在一个方法的实现里,使用了__block,则:

  • 通过引用的方式访问对象的,self被retain;
    <span class="pln">dispatch_async</span><span class="pun">(</span><span class="pln">queue</span><span class="pun">,</span><span class="pln"> </span><span class="pun">^{</span><span class="pln">    </span><span class="com">// instanceVariable is used by reference, self is retained</span><span class="pln">    doSomethingWithObject</span><span class="pun">(</span><span class="pln">instanceVariable</span><span class="pun">);</span><span class="pln"></span><span class="pun">});</span>
  • 通过值访问对象的,被访问的对象被retain;
    <span class="pln">id localVariable </span><span class="pun">=</span><span class="pln"> instanceVariable</span><span class="pun">;</span><span class="pln">dispatch_async</span><span class="pun">(</span><span class="pln">queue</span><span class="pun">,</span><span class="pln"> </span><span class="pun">^{</span><span class="pln">    </span><span class="com">// localVariable is used by value, localVariable is retained (not self)</span><span class="pln">    doSomethingWithObject</span><span class="pun">(</span><span class="pln">localVariable</span><span class="pun">);</span><span class="pln"></span><span class="pun">});</span>

__block和C++对象之间的关系

需要注意两点:

  • 把一个基于栈的C++对象变成一个__block类型的时候,要调用类的copy constructor;
  • 在block内部使用栈中的C++对象时,要调用栈的const copy constructor;
Comment by project member SuPeiqi....@gmail.com, Mar 10, 2011

change :

<span class="kwd">int</span><span class="pln"> </span><span class="pun">(</span><span class="pln">myBlock</span><span class="pun">)(</span><span class="kwd">int</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> num</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> num </span><span class="pun">^</span><span class="pln"> multipiler</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span>

to :

<span class="kwd">int</span><span class="pln"> </span><span class="pun">(</span><span class="pln">myBlock</span><span class="pun">)(</span><span class="kwd">int</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> num</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> num </span><span class="pun">*</span><span class="pln">  multipiler</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}来自:http://code.google.com/p/nevel-mercury/wiki/GetFamiliarWithBlock</span>
0 0