IOS中Block用法介绍
1 什么是block
Block 是iOS在4.0之后新增的程式语法,严格来说block的概念并不算是基础程式设计的范围.Block是C级别的语法和运行时特性。Block比较类似C函数,但是Block比之C函数,其灵活性体现在栈内存、堆内存的引用,我们甚至可以将一个Block作为参数传给其他的函数或者Block。
引用网上例子:
1: int multiplier = 7 ;
2: int (^myBlock)( int ) = ^( int num)
3: {
4: return num * multiplier;
5: };
语法介绍:
我们使用「^」运算子来宣告一个block变数,而且在block的定义最后面要加上「;」来表示一个完整的述句.
我们宣告一个「myBlock」变数,用「^」符号来表示这是一个block。
这是block的完整定义,这个定义将会指定给「myBlock」变数。
表示「myBlock」是一个回传值为整数(int)的block。
它有一个参数,型态也是整数。
这个参数的名字叫做「num」。
这是block的内容。
- typedef void (^BoolBlock)(BOOL);//一个只接受一个BOOL参数,没有返回值的block
- typedef int (^IntBlock)(void);//一个没有参数,返回int的block
- typedef BoolBlock (^HugeBlock)(IntBlock);//看看,这个HugeBlock的参数和返回值都是block
- - (void)someMethod
- {
- BoolBlock ablock = ^(BOOL bValue) {
- NSLog(@"Bool block!");
- };
- ablock();
- }
__block 变量:
在block内只能读取在同一个作用域的变数而且没有办法修改在block外定义的任何变数,此时若我们想要这些变数能够在block中被修改,就必须在前面挂上__block的修饰词,
1: __block int multiplier = 7 ;
2: int (^myBlock)( int ) = ^( int num)
3: {
4: if (num > 5 )
5: {
6: multiplier = 7 ;
7: }
8: else
9: {
10: multiplier = 10 ;
11: }
12: return num * multiplier;
13: };
Block 概要
Block 提供我们一种能够将函数程式码内嵌在一般述句中的方法,在其他语言中也有类似的概念称做「closure」,但是为了配合Objective-C的贯例,我们一律将这种用法称为「block」
Block 的功能
Block 是一种具有匿名功能的内嵌函数,它的特性如下:
如一般的函数般能拥有带有型态的参数。
拥有回传值。
可以撷取被定义的词法作用域(lexical scope)状态。
可以选择性地修改词法作用域的状态。
注:词法作用域(lexical scope)可以想像成是某个函数两个大括号中间的区块,这个区块在程式执行时,系统会将这个区块放入堆叠记忆体中,在这个区块中的宣告的变数就像是我们常听到的区域变数,当我们说block可以撷取同一词法作用域的状态时可以想像block变数和其他区域变数是同一个层级的区域变数(位于同一层的堆叠里),而block的内容可以读取到和他同一层级的其他区域变数。
我们可以拷贝一个block,也可以将它丢到其他的执行绪中使用,基本上虽然block在iOS程式开发中可以使用在C/C++开发的程式片段,也可以在Objective-C中使用,不过在系统的定义上,block永远会被视为是一个Objective-C的物件。
Block 的使用时机
Block 一般是用来表示、简化一小段的程式码,它特别适合用来建立一些同步执行的程式片段、封装一些小型的工作或是用来做为某一个工作完成时的回传呼叫(callback) 。
在新的iOS API中block被大量用来取代传统的delegate和callback,而新的API会大量使用block主要是基于以下两个原因:
可以直接在程式码中撰写等会要接着执行的程式,直接将程式码变成函数的参数传入函数中,这是新API最常使用block的地方。
可以存取区域变数,在传统的callback实作时,若想要存取区域变数得将变数封装成结构才能使用,而block则是可以很方便地直接存取区域变数。
宣告和建立Block
宣告Block的参考(Reference)
Block 变数储存的是一个block的参考,我们使用类似宣告指标的方式来宣告,不同的是这时block变数指到的地方是一个函数,而指标使用的是「*」,block则是使用「^」来宣告,下面是一些合法的block宣告:
1: /* 回传void ,参数也是void 的block*/
2: void (^blockReturningVoidWithVoidArgument)( void );
3: /* 回传整数,两个参数分别是整数和字元型态的block*/
4: int (^blockReturningIntWithIntAndCharArguments)( int , char );
5: /* 回传void ,含有10 个block 的阵列,每个block 都有一个型态为整数的参数*/
6: void (^arrayOfTenBlocksReturningVoidWinIntArgument[ 10 ])( int );
7: X.3.2 建立一个Block
9: 我们使用「^」来开始一个block,并在最后使用「;」来表示结束,下面的范例示范了一个block变数,然后再定义一个block把它指定给block变数:
10:
11: int (^oneFrom)( int ); /* 宣告block 变数*/
12: /* 定义block 的内容并指定给上面宣告的变数*/
13: oneFrom = ^(int anInt)
14: {
15: return anInt = - 1 ;
16: };
将Block当作函数的参数
我们可以像使用一般函数使用参数的方式,将block以函数参数的型式传入函数中,在这种情况下,大多数我们使用block的方式将不会倾向宣告block而是直接以内嵌的方式来将block传入,这也是目前新版SDK中主流的做法,我们将补充前面章节的例子来说明:
1: char *myCharacters[ 3 ] = { "TomJohn" , "George" , "Charles Condomine" };
2: qsort_b (myCharacters, 3 , sizeof ( char *),
3: ^( const void *l, const void *r)
4: {
5: char *left = *( char **)l;
6: char *right = *( char **)r;
7: return strncmp (left, right, 1 );
8: } // 这里是block 的终点。
9: );
10: // 最后的结果为:{"Charles Condomine", "George", "TomJohn"}
在上面的例子中,block本身就是函数参数的一部分,在下一个例子中dispatch_apply函数中使用block,dispatch_apply的定义如下:
1: void
2: dispatch_apply( size_t iterations, dispatch_queue_t queue, void (^block)( size_t ));
3: 这个函数将一个block提交到发送伫列(dispatch queue)中来执行多重的呼叫,只有当伫列中的工作都执行完成后才会回传,这个函数拥有三个变数,而最后一个参数就是block ,请参考下面的范例:
4:
5: size_t count = 10 ;
6: dispatch_queue_t queue =
7: dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT , 0 );
8: dispatch_apply (count, queue, ^( size_t i) {
9: printf ( "%u\n" , i);
10: });
将Block当作方法的参数
在SDK中提供了许多使用block的方法,我们可以像传递一般参数的方式来传递block,下面这个范例示范如何在一个阵列的前5笔资料中取出我们想要的资料的索引值:
1: // 所有的资料
2: NSArray *array = [ NSArray arrayWithObjects : @"A" , @"B" , @"C" , @"A" , @"B" , @"Z" , @"G" , @"are" , @" Q" ,nil ];
3: // 我们只要这个集合内的资料
4: NSSet *filterSet = [ NSSet setWithObjects : @"A" , @"B" , @"Z" , @"Q" , nil ];
5: BOOL (^test)( id obj, NSUInteger idx, BOOL *stop);
6: test = ^ ( id obj, NSUInteger idx, BOOL *stop) {
7: // 只对前5 笔资料做检查
8: if (idx < 5 ) {
9: if ([filterSet containsObject : obj]) {
10: return YES ;
11: }
12: }
13: return NO ;
14: };
15: NSIndexSet *indexes = [array indexesOfObjectsPassingTest :test];
16: NSLog ( @"indexes: %@" , indexes);
17: // 结果:indexes: <NSIndexSet: 0x6101ff0>[number of indexes: 4 (in 2 ranges), indexes: (0-1 3-4)]
18: // 前5笔资料中,有4笔符合条件,它们的索引值分别是0-1, 3-4