IOS block 教程
IOS block 教程
http://pernghh.pixnet.net/blog/trackback/eac87d412e/33563409
本文来自台湾的某开发人员的博客,被墙,感觉讲的比较易懂,所以引过来。文字简体化了,原来是繁体,变数=变量,这个注意一下。
本章学习目标:
1. 了解何谓block。
2. 了解block的使用方法。
Block 是iOS在4.0之后新增的程式语法,严格来说block的概念并不算是基础程式设计的范围,对初学者来说也不是很容易了解,但是在iOS SDK 4.0之后,block几乎出现在所有新版的API之中,换句话说,如果不了解block这个概念就无法使用SDK 4.0版本以后的新功能,因此虽然block本身的语法有点难度,但为了使用iOS的新功能我们还是得硬着头皮去了解这个新的程式概念。
在这一章的目标以了解如何使用block为主而不深入探讨block底层的运作方式,至于有些初学者较少遇到的辞汇如「词法作用域(lexical scope)」等,本章将不再多做解释,待有兴趣的读者去请教Google大神吧。
X.1 初探Block
在这一小节我们先用一些简单范例来导入block的概念。
X.1.1 宣告和使用Block
我们使用「^」运算子来宣告一个block变数,而且在block的定义最后面要加上「;」来表示一个完整的述句(也就是将整个blo??ck定义视为前面章节所介绍的简单述句,因为整个定义必须是一个完整的句子,所以必须在最后面加上分号),下面是一个block的范例:
1: int multiplier = 7 ; 2: int (^myBlock)( int ) = ^( int num) 3: { 4: return num * multiplier; 5: };
1: int multiplier = 7 ; 2: int (^myBlock)( int ) = ^( int num) 3: { 4: return num * multiplier; 5: }; 6: printf ( "%d" , myBlock( 3 )); 7: //结果会打印出21
1: char *myCharacters[ 3 ] = { "TomJohn" , "George" , "Charles Condomine" }; 2: qsort_b (myCharacters, 3 , 3: sizeof ( char *), 4: ^( const void *l, const void *r)//block部分 5: { 6: char *left = *( char **)l; 7: char *right = *( char **)r; 8: return strncmp (left, right, 1 ); 9: } //end 10: );
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: };
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 8: 9: 我们使用「^」来开始一个block,并在最后使用「;」来表示结束,下面的范例示范了一个block变数,然后再定义一个block把它指定给block变数: 10: 11: int (^oneFrom)( int ); /* 宣告block 变数*/ 12: /* 定义block 的内容并指定给上面宣告的变数*/ 13: oneFrom = ^(int anInt) 14: { 15: return anInt = - 1 ; 16: };
1: int GlobalInt = 0 ; 2: int (^getGlobalInt)( void ) = ^ ( void ) { return GlobalInt ;};
1: int x = 123 ; 2: void (^printXAndY)( int ) = ^( int y) 3: { 4: printf ( "%d %d\n" , x, y); 5: }; 6: // 将会印出123 456 7: printXAndY( 456 ); 8: 就如上面第三点所提到的,在上例中的int x = 123的变量x,在传入block后将视同常数,因此若我们在block中试着去修改x的值时就会产生错误,下面的例子将会无法通过编译: 9: 10: int x = 123 ; 11: void (^printXAndY)( int ) = ^( int y) 12: { 13: // 下面这一行是错的,因为x 在这是一个常数不能被修改。 14: x = x + y; 15: printf ( "%d %d\n" , x, y); 16: };
1: // 加上__block 修饰词,所以可以在block 中被修改。 2: __block int x = 123 ; 3: void (^printXAndY)( int ) = ^( int y) 4: { 5: x = x + y; 6: printf ( "%d %d\n" , x, y); 7: }; 8: // 将会印出579 456 9: printXAndY( 456 ); 10: //x 将会变成 579; 11: 下面我们使用一个范例来介绍各类型的变数和block之间的互动: 12: 13: extern NSInteger CounterGlobal; 14: static NSInteger CounterStatic; 15: { 16: NSInteger localCounter = 42 ; 17: __block char localCharacter; 18: void (^aBlock)( void ) = ^( void ) 19: { 20: ++ CounterGlobal ; //可以存取。 21: ++ CounterStatic ; //可以存取。 22: CounterGlobal = localCounter; //localCounter在block 建立时就不可变了。 23: localCharacter = 'a' ; //设定外面定义的localCharacter 变数。 24: }; 25: ++localCounter; //不会影响的block 中的值。 26: localCharacter = 'b' ; 27: aBlock(); //执行block 的内容。 28: //执行完后,localCharachter 会变成'a' 29: }
1: dispatch_async (queue, ^{ 2: // 因为直接存取实体变数instanceVariable ,所以self 的retain count 会加1 3: doSomethingWithObject (instanceVariable); 4: }); 5: id localVaribale = instanceVariable; 6: dispatch_async (queue, ^{ 7: //localVariable 是存取值,所以这时只有localVariable 的retain count 加1 8: //self 的 return count 并不会增加。 9: doSomethingWithObject (localVaribale); 10: });
1: int (^oneFrom)( int ) = ^( int anInt) { 2: return anInt - 1 ; 3: }; 4: printf ( "1 from 10 is %d" , oneFrom( 10 )); 5: //结果会显示:1 from 10 is 9 6: float (^distanceTraveled)( float , float , float ) = ^( float startingSpeed, float acceleration, float time) 7: { 8: float distance = (startingSpeed ??* time) + ( 0.5 * acceleration * time * time); 9: return distance; 10: }; 11: float howFar = distanceTraveled( 0.0 , 9.8 , 1.0 ); 12: //howFar会变成4.9
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"}
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: });
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
1: // 这是错误的范例,请勿在程式中使用这些语法!! 2: void dontDoThis() { 3: void (^blockArray[3])(void); // 3 个block 的阵列 4: for (int i = 0; i < 3; ++i) { 5: blockArray[i] = ^{ printf("hello, %d\n", i); }; 6: // 注意: 这个block 定义仅在for 回圈有效。 7: } 8: } 9: void dontDoThisEither() { 10: void (^block)(void); 11: int i = random(): 12: if (i > 1000) { 13: block = ^{ printf("got i at: %d\n", i); };