简介
代码块是一种轻量级的匿名函数,可以在代码块中创建和传递可执行的代码片段,因为代码块是基于C语言的闭包概念,所以允许在代码块中捕获变量,形成闭包环境。
使用
声明并定义一个代码块(这个代码块不带参数,没有返回值)
//声明并定义一个代码块
void (^myBlock)(void) = ^(){
NSLog(@"这是代码看执行的");
};
//执行代码块
myBlock();
!!!注意:大括号后面记得添加 ‘;’ 号
声明一个带参数和返回值的代码块
int (^addBlock)(int, int) = ^(int a, int b){
return a + b;
};
int sum = addBlock(2, 3);
NSLog(@"%d", sum);
输出:5
代码块声明格式:
ReturnType (^BlockName)(ParameterTypes)
// ReturnType:代码块的返回类型。
// BlockName:为代码块类型创建的别名。
// ParameterTypes:代码块的参数类型列表,没有参数里面可填void或不填,多个参数类型用‘,’隔开。
代码块定义格式:
^(NSInteger a, NSInteger b) {
return a + b;
};
// 1.以‘^’开头
// 2.‘()’里和函数定义一样写传入的参数,不传参可以不要‘()’
// 3.‘{}’里写执行的语句
!!!注意:大括号后面记得添加 ‘;’ 号
变量捕获
变量捕获允许在代码块中引用代码块外部的变量
两种捕获方式:
- 自动捕获
- 静态捕获
1.自动捕获:
代码块可以捕获在其作用域内定义的变量
下面例子中会捕获变量,获取变量的值:
int i = 10;
void (^printIBlock)(void) = ^{
NSLog(@"i = %d", i);
};
printIBlock();
输出:i = 10
代码块修改捕获的变量:
// 需要加__block,不然会报错
__block int i = 10;
void (^setIBlock)(void) = ^{
i = 20;
NSLog(@"setIBlock1: i = %d", i);
};
setIBlock();
NSLog(@"main: i = %d", i);
输出:
setIBlock1: i = 20
main: i = 20
如果需要修改基本数据类型(int、float…),需要在变量声明前加“__block”
代码块获取和修改对象的值:
NSMutableArray *arr = [NSMutableArray arrayWithArray: @[@"a", @"b", @"c"]];
NSLog(@"%p: %@", arr, arr);
void (^addArrBlock)(void) = ^{
[arr addObject:@"d"];
NSLog(@"%p: %@", arr, arr);
};
addArrBlock();
NSLog(@"%p: %@", arr, arr);
输出:
0x600000c00c90: (a,b,c)
0x600000c00c90: (a,b,c,d)
0x600000c00c90: (a,b,c,d)
如果是对象的,指向听一个地址。
2.静态捕获:
static int n = 5;
void (^staticBlock)(void) = ^{
n = 8;
NSLog(@"n = %d", n);
};
staticBlock();
输出:n = 8
静态捕获都是直接引用外部变量
代码块copy
通常情况下,创建代码块会存储在栈空间,如果代码块以参数的形式传递给另一个方法,然后代码块声明的作用域失效了,那么代码块的空间会被释放,在别的方法调用就会出现问题。
使用copy把代码块拷贝到堆空间就不会出现下面的上面的问题。
下面两个Demo是在MRC模式下运行的,ARC模式下都是正常的,应该是ARC做了其他处理。
不使用copy:
void (^getblock(void))(void) {
// 方法执行完后会被释放
int a = 3;
void (^block)(void) = ^{
NSLog(@"%d", a);
};
return block;
}
int main {
void (^block)(void) = getblock();
block();
}
输出:9375
上面Demo中,当getblock方法执行完后里面的东西会被释放,所以在main方法里执行block代码块时打印的是错误的值
使用copy:
void (^getblock(void))(void) {
// 方法执行完后会被释放
int a = 3;
void (^block)(void) = ^{
NSLog(@"%d", a);
};
// 使用copy把block拷贝到堆空间,copyblock指向拷贝后的地址
void (^copyblock)(void) = [block copy];
return copyblock;
}
int main {
void (^block)(void) = getblock();
block();
}
输出:3
上面Demo中,执行了[block copy]方法,把代码块拷贝到堆空间,然后返回代码块在堆空间的地址,所以输出正常
注:栈空间作用域结束后会自动释放内容,堆空间需要手动释放
代码块与内存管理
在ARC模式下需要注意一个循环应用的问题。
当在代码块内部使用代码块外的对象时,引用计数器会+1。这就需要注意循环引用问题,如果不想引用计数器+1,可以使用 __weak。
__weak typeof(self) weakSelf = self;
void (^block2)(void) = ^{
// 在代码块里使用weakSelf代替self
weakSelf
};