块
基本语法
定义块的语法格式:
^ [块返回值类型] (形参类型1 形参1, 形参类型2 形参2, ...){
//块执行体
}
定义块的语法与定义函数的语法存在如下差异:
- 定义块必须以^开头;
- 定义块的返回值类型可以省略;
- 定义块无须指定名字;
- 如果块没有参数,参数部分的括号不可省略,但可以留空,建议以void作为占位符。
若需多次调用已经定义的块,可定义一个块变量,将该块赋给块变量,通过块变量来调用块。定义块变量的格式:
块返回值类型 (^块变量名) (形参类型1, 形参类型2, ...);
块与匿名函数类似,块变量与函数指针类似。
块与局部变量
- 对于没有用
__block
修饰的局部变量,块只能访问它,但不可修改它;且在定义块时就已经将这种局部变量的值保存在块中,而不是等到调用块时才去访问、获取它的值,因此,后续程序对局部变量的值的修改,对块没有影响,即便这种修改在调用块之前。#import <Foundation/Foundation.h> int main(){ @autoreleasepool{ int my = 20; void (^printMy)(void) = ^(void){ //尝试对局部变量赋值,程序会报错 my = 30; // ① NSLog(@"%d", my); //访问局部变量是可以的 }; my = 45; printMy(); //调用块 } }
将①处代码注释掉,再编译运行上述程序,会输出20,而非45。
- 对于
__block
修饰的局部变量,块内可以修改它;并且,无论何时,块都会直接使用该局部变量本身,而不是将局部变量的值复制到块内。#import <Foundation/Foundation.h> int main(int argc, char* argv[]){ @autoreleasepool{ __block int my = 20; void (^printMy)(void) = ^(void){ NSLog(@"%d", my); my = 30; NSLog(@"%d", my); }; my = 45; printMy(); NSLog(@"%d", my); } }
上述程序三次分别输出45、30、30。
块作为函数参数
块作为函数参数时,无需定义块变量,直接把块作为参数传递给函数即可。出于可读性考虑,通常将块作为函数的最后一个参数。
使用typedef定义块类型
语法格式:
typedef 块返回值类型 (^块类型)(形参类型1 [形参名], 形参类型2 [形参名], ...);
用途:
- 复用块类型,使用块类型可以更方便地定义块变量;
#import <Foundation/Foundation.h> int main(int argc, char* argv[]){ @autoreleasepool{ //定义了一个返回类型为void、形参为NSString*的块类型FKPrintBlock typedef void (^FKPrintBlock)(NSString*); //使用块类型定义块变量,并将指定块赋给块变量 FKPrintBlock print = ^(NSString* info){ NSLog(@"%@", info); }; //使用块类型定义块变量,并将指定块赋给块变量 FKPrintBlock loopPrint = ^(NSString* info){ for(int i = 0; i < 3; ++i){ NSLog(@"%@", info); } }; //一次调用两个块 print(@"Objective-C"); loopPrint(@"iOS"); } }
- 使用块类型定义函数参数。
#import <Foundation/Foundation.h> typedef void (^FKProcessBlock)(int); //定义一个块类型 //使用FKProcessBlock定义最后一个参数类型为块 void processArray(int array[], unsigned int len, FKProcessBlock process){ for(int i = 0; i < len; ++i){ //将数组元素作为参数调用块 process(array[i]); } } int main(int argc, char* argv[]){ @autoreleasepool{ int arr[] = {2, 4, 6}; //传入块作为参数调用processArray()函数 processArray(arr, 3, ^(int num){ NSLog(@"元素平方为:%d", num * num); }); } }