第37条:理解“块”这一概念

Posted CHM

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第37条:理解“块”这一概念相关的知识,希望对你有一定的参考价值。

  第6章

  block 与 GCD

  当前在开发应用程序时,每位程序员都应该留意多线程问题。你可能会说自己要开发的应用程序用不到多线程,即便如此,它也很可能是多线程的,因为系统框架通常会在 UI 线程之外再使用一些线程来执行任务。开发应用程序时,最糟糕的事情莫过于程序因 UI 线程阻塞而挂起了。在 Mac OS X 系统中,这将使鼠标指针一直呈现令人焦急的旋转彩球状;而在 ios 系统中,阻塞过久的程序可能会终止执行。

  所幸苹果公司以全新方式设计了多线程。当前多线程编程的核心就是 “块”(block)与 GCD(Grand Central Dispatch, GCD)。这虽然是两种不同的技术,但它们是一并引入的。“块”是一种可在 C 、C++及 Objective-C 代码中使用的 “词法闭包”(lexical closure),它极为有用,这主要是因为借由此机制,开发者可将代码像对象一样传递,令其在不同环境(context)下运行。还有个关键的地方是,在定义“块”的范围内,它可以访问到其中的全部变量。

  GCD 是一种与块有关的技术,它提供了对线程的抽象,而这种抽象则基于“派发队列”(dispatch queue)(亦称调度队列)。开发者可将块排入队列中,由 GCD 负责处理所有的调度事宜。GCD 会根据系统资源情况,适时地创建、复用、摧毁后台线程(background thread),以便处理每个队列。此外,使用 GCD 还可以方便地完成常见编程任务,比如编写 “只执行一次的线程安全代码”(thread-safe single-code execution),或者根据可用的系统资源来并发执行多个操作。

  block 与 GCD 都是当前 Objective-C 编程的基石。因此,必须理解其工作原理及功能。

  本条要点:(作者总结)

 

  块可以实现闭包。这项语言特性是作为“扩展”(extension)而加入 GCC 编译器中的,在近期版本的 Clang 中都可以使用 (Clang 是开发 Mac OS X 及iOS 程序所用的编译器)。10.4 版及其后的 Mac  OS X 系统,与 4.0 版及其后的iOS 系统中,都含有正常执行块所需的运行期组件。从技术上讲,这是个位于C 语言层面的特性,因此,只要有支持此特性的编译器,以及能执行块的运行期组件,就可以在 C、C++、Objective-C 、Objective-C++ 代码中使用它。

块的基础知识

  块与函数类似,只不过是直接定义在另一个函数里的,和定义它的那个函数共享同一个范围内的东西。块用 “^” 符号(caret,可称为脱字符或插入符)来表示,后面跟着一对花括号,括号里面是块的实现代码,例如,下面就是个简单的块:

1   ^{
2 
3     // Block implementation here
4   }

  块其实就是个值,而且自有其相关类型。与 int、float 或 Objective-C 对象一样,也可以把块赋给变量,然后像使用其他变量那样使用它。块类型的语法与函数指针近似。下面列出的这个块很简单,没有参数,也不返回值:

1   void (^someBlock)() = ^{
2 
3     // Block implementation here
4   };

   这段代码定义了一个名为 someBlock 的变量。由于变量名写在正中间,所以看上去也许有点怪,不过一旦理解了语法,很容易就能读懂。块类型的语法结构如下:

1 return_type (^block_name) (parameters)

  下面这种写法所定义的块,返回 int 值,并且接受两个 int 做参数:

1   int (^addBlock)(int a, int b) = ^(int a, int b){
2 
3     return a + b;
4   }

  定义好之后,就可以像函数那样使用了。比方说,addBlock 块可以这样用:

  int add = addBlock(2, 5); // < add = 7

  块的强大之处是:在声明它的范围里。所有变量都可以为其所捕获。这也就是说,那个范围里的全部变量,在块里依然可用。比如,下面这段代码所定义的块,就使用了块以外的变量:

1   int additional = 5;
2 
3   int (^addBlock)(int a, int b) = ^(int a, int b){
4 
5     return a + b + addItional;
6   };
7 
8   int add = addBlock(2, 5); // < add = 12

  默认情况下,为块所捕获的变量,是不可以在块里修改的。在本例中,假如块内的代码改动了 additional 变量的值,那么编译器就会报错。不过,声明变量的时候可以加上 __block 修饰符,这样就可以在块内修改了。例如,可以用下面这个块来枚举数组中的元素,以判断其中有多少个小于 2 的数:

 1   NSArray *array = @[@0, @1, @2, @3, @4, @5];
 2 
 3   __block NSInteger count = 0;
 4 
 5   [array enumerateObjectsUsingBlock:
 6 
 7     ^(NSNumber *number, NSUInteger idx, BooL *stop) {
 8 
 9       if([number compare:@2] == NSOrderedAscending) {
10         count++;
11       }
12   }] ;
13   // count = 2

  这段范例代码也演示了 “内联块”(inline Block)的用法。

 

 

 

 

 

 

 

 

 

 

 

  

 

以上是关于第37条:理解“块”这一概念的主要内容,如果未能解决你的问题,请参考以下文章

《Effective Objective-C 2.0》读后总结 之四

第6条:理解“属性”这一概念(中)

-对象消息运行期)第6条:理解“属性”这一概念

《Effective Objective-C 2.0》读书/实战笔记 三

需要一种有效的方法来避免使用 Laravel 5 重复代码片段

[Effective JavaScript 笔记] 第12条:理解变量声明提升