文章目录
块
块的概念
块(Block)是Objective-C对ANSIC所做的扩展, 使用块可以更好地简化Objective-C编程, 而且Objective-C的很多都API都依赖于块
块的基本语法
objective-c
^[块返回值类型] (形参类型1 形参1, 形参类型2 形参2, ...) {
块执行体
}
从上面的已语法格式可以看出, 定义块的语法非常像定义一个函数, 但知识定义一个匿名函数, 但定义块与定义函数的语法格式存在如下差异:
定义块必须以
^开头定义块的返回值可以省略, 而且经常都会省略声明块的返回值类型
定义块无须指定名字
如果块没有返回值, 块无须带参数, 通常建议使用``void` 作为占位符
如果程序需要以后多次调用已经定义的块, 那么程序应该将块赋给一个块变量, 定义块变量的语法格式如下:
objective-c
块返回值类型 (^块变量名)(形参类型1 形参1, 形参类型2 形参2, ...);
定义块变量时, 无须声明形参名, 只要指定形参类型即可, 类似的, 如果该块不需要形参, 则建议使用 void 作为占位符
例如: 下面是有参和无参两种块的的定义和调用
objective-c
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 定义不带参数, 无返回值的块
void (^printStr)(void) = ^void (void) {
NSLog(@"我正在开始学习");
};
// 使用printStr调用块
printStr();
// 定义带参数, 有返回值的块
double (^hypot)(double, double) = ^double (double num1, double num2) {
return sqrt(num1 * num1 + num2 * num2);
};
// 调用块,并输出块的返回值
NSLog(@"%g", hypot(3, 4));
// 也可以先只定义块变量, 定义带参数, 无返回值的块
void (^print)(NSString* info);
print = ^void (NSString* info) {
NSLog(@"info 的参数为: %@", info);
};
// 调用块
print(@"hello");
}
return EXIT_SUCCESS;
}
块与局部变量
块可以快速访问程序中局部变量的值, 当块访问局部变量的值的时候, 不允许修改局部变量的值
objective-c
#import<Foundation/Foundation.h>
int main(int argc, const char* argv[]) {
@autoreleasepool {
int my = 20; // 定义局部变量
void (^printmy)(void) = ^void(void) {
my = 30; // 出错, 不可以修改
NSLog(@"%d", my);
};
printmy();
}
return 0;
}
objective-c
#import<Foundation/Foundation.h>
int main(int argc, const char* argv[]) {
@autoreleasepool {
int my = 20; // 定义局部变量
void (^printmy)(void) = ^void(void) {
NSLog(@"%d", my);
};
my = 30;
printmy();
}
return 0;
}
objective-c
输出: 20
上面程序仍然输出20, 而不是30, 这是因为当程序使用块访问局部变量的时候, 系统在定义块的时候就会把局部变量的值保存在块中, 而不是等到执行的时候在去访问, 获取变量的值. 上面的程序虽然将my变量的值赋值为30, 但这条赋值语句位于块定义之后, 因此, 在块定义中my变量的值已经固定为20, 后面程序对my变量进行修改后, 对块不存在任何影响
如果不想要在定义块的时候就把局部变量的值复制到块中, 而是等到执行的时候再去访问,获取局部变量的值, 甚至希望块也可以改变局部变量的值, 此时可以考虑使用__block(注意这里block前面是两个"_")修饰局部变量
__block修饰后可以在块内修改局部变量的值
objective-c
#import<Foundation/Foundation.h>
int main(int argc, const char* argv[]) {
@autoreleasepool {
// 用__block修饰局部变量
__block int my = 20;
void (^printmy)(void) = ^void (void) {
my = 30;
NSLog(@"%d", my);
};
printmy();
my = 45;
printmy();
}
return 0;
}
objective-c
输出 : 30
30
在调用块的时候再去获取局部变量的值
objective-c
#import<Foundation/Foundation.h>
int main(int argc, const char* argv[]) {
@autoreleasepool {
// 用__block修饰局部变量
__block int my = 20;
void (^printmy)(void) = ^void (void) {
NSLog(@"%d", my);
};
printmy();
my = 45;
printmy();
}
return 0;
}
objective-c
输出: 20
45
直接使用块作为参数
当把块作为方法参数的时候, 无须定义块变量, 直接把块作为参数传给指定的方法即可,需要指出的是, 块只能作为方法的最后一个参数
使用typedef定义块变量类型
使用typedef可以定义块变量类型, 一旦定义了块变量类型, 该块变量主要有两个用途:
- 复用块类型变量, 使用块变量类型可以重复定义多个块变量
- 使用块变量类型定义函数参数, 这样即可定义带块参数的函数
objective-c
#import<Foundation/Foundation.h>
int main(int argc, const char* argv[]) {
@autoreleasepool {
typedef void (^FKPrintBlock) (NSString*);
FKPrintBlock print = ^void(NSString* info) {
NSLog(@"%@", info);
};
FKPrintBlock loopPrint = ^void(NSString* info) {
for (int i = 0; i < 3; i++) {
NSLog(@"%@", info);
}
};
print(@"hello world");
loopPrint(@"hehe");
}
return 0;
}
利用typedef定义的块变量类型即可为函数声明块变量类型的形参, 这要求调用函数时必须传入块变量
objective-c
#import<Foundation/Foundation.h>
typedef void (^FKPrintBlock)(int);
// 使用FKPrintBlock定义最后一个参数类型为块
void processArray(int array[], unsigned int len, FKPrintBlock process) {
for (int i = 0; i < len; i++) {
process(array[i]); // 将数组元素作为参数调用块
}
}
int main(int argc, const char* argv[]) {
@autoreleasepool {
int arr[] = {2, 3, 4};
processArray(arr, 3, ^(int num) {
NSLog(@"元素平方为: %d", num * num);
});
}
return 0;
}