Block在iOS开发中经常用到,苹果官方推荐使用block,好用灵活
平时执行一段代码,把这段代码放到方法中,调用方法可以执行这一段代码,block也是一样的,把一段代码封装到block中,可以在任何时候调用block,执行block中的代码
Block
是OC中的一种数据类型
是一个能工作的代码单元
,可以在任何需要的时候被执行
本质上是轻量级的匿名函数
,可以作为其他函数的参数
或者返回值
块代码本身可能有一个参数列表
,也可能有一个返回值
可以把块代码赋给一个变量,并在需要的时候调用,就像调用一个普通函数一样
从Xcode 4.0
开始,系统类库中的函数越来越多的开始使用Block作为参数,以下是在系统函数中使用块代码的部分情况:
遍历数组或者字典
排序
视图动画
结束回调
错误处理
多线程
……
默认情况下,不允许在块代码内部修改外部变量的数值 __block
循环引用 __weak(iOS5.0以下版本使用__unsafe_unretained)
定义:
^是Block的特有标记
Block的实现代码包含在{}之间
大多情况下,以内联inline函数的方式被定义和使用
Block与C语言的函数指针有些相似,但使用起来更加灵活
示例:
void(^demoBlock)() = ^ {
NSLog(@"demo Block");
};
int(^sumBlock)(int, int) = ^(int x, int y) {
return x + y;
};
格式说明:
(返回类型)(^块名称)(参数类型列表) = ^(形参列表) {代码实现};
如果没有参数,等号后面参数列表的()可以省略
注意:
Block可以使用在定义之前声明的局部变量
在定义Block时,会在Block中建立当前局部变量内容的副本(拷贝)
后续再对该变量的数值进行修改,不会影响Block中的数值
如果需要在block中保持外部变量的数值变化,需要使用__block关键字
使用__block关键字后,同样可以在Block中修改该变量的数值
Block可以被当做参数直接传递
// 说明:遍历并NSLog() array中的内容,当obj 为"王五"时停止遍历
NSArray *array = @[@"张三", @"李四", @"王五", @"赵六"];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(@"第 %d 项内容是 %@", (int)idx, obj);
if ([@"王五" isEqualToString:obj]) {
*stop = YES;
}
}];
在被当做参数传递时,Block同样可以使用在定义之前声明的局部变量
int stopIndex = 1;
NSArray *array = @[@"张三", @"李四", @"王五", @"赵六"];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(@"第 %d 项内容是 %@", (int)idx, obj);
if ([@"王五" isEqualToString:obj] || idx == stopIndex) {
*stop = YES;
}
}];
注意,默认情况下,Block外部的变量,在Block中是只读的!
__block
关键字:要修改Block之外的局部变量,需要使用__block关键字
__block BOOL flag = NO;
int stopIndex = 1;
NSArray *array = @[@"张三", @"李四", @"王五", @"赵六"];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(@"%@",obj);
if ([@"王五" isEqualToString:obj] || idx == stopIndex) {
*stop = YES;
flag = YES; // 现在可以修改了!!!
}
}];
//2016-08-06 14:56:10.890 Objective-C-Block[24244:332746] 张三
//2016-08-06 14:56:10.891 Objective-C-Block[24244:332746] 李四
//Program ended with exit code: 0
无需使用__block关键字,在块代码中可以修改成员变量的数值(比较少用)
对象传递进Block的方式
为保证Block中的代码正常运行,在将name的指针传递给Block时,Block会自动对name的指针做强引用
NSMutableString *name = [NSMutableString stringWithString:@"李四"];
NSLog(@"out-block %p %p %@", name, &name, name);
void(^myBlock)() = ^ {
[name setString:@"张三"];
NSLog(@"in-block %p %p %@", name, &name, name);
};
myBlock();
[name setString:@"王五"];
myBlock();
NSLog(@"out-block %p %p %@", name, &name, name);
typedef
定义block类型的变量
可以使用typedef定义一个Block的类型,便于在后续直接使用
typedef是关键字用于定义类型,MyBlock是定义的Block类型
area、sum分别是MyBlock类型的两个Block变量
把block定义为一种类型 就可以使用这种类型声明无限多个block变量 使用变量名来调用
typedef double(^MyBlock)(double, double);
//使用typedef将block定义为一种类型,
//就可以使用这种类型声明block变量;
//MyBlock 就是一种变量类型了
MyBlock area = ^(double x, double y) {
return x * y;
};
MyBlock sum = ^(double a, double b) {
return a + b;
};
NSLog(@"%.2f", area(10.0, 20.0));
NSLog(@"%.2f", sum(10.0, 20.0));
//2016-08-06 15:42:05.787 Objective-C-Block[26973:372574] 200.00
//2016-08-06 15:42:05.788 Objective-C-Block[26973:372574] 30.00
尽管,typedef可以简化Block的定义,但在实际开发中并不会频繁使用typedef关键字
这是因为Block具有非常强的灵活性,尤其在以参数传递时,使用Block的目的就是为了立即使用
既然Block是一种数据类型,那么可以将Block当做比较特殊的对象
#pragma mark 定义并添加到数组
@property (nonatomic, strong) NSMutableArray *myBlocks;
int(^sum)(int, int) = ^(int x, int y) {
return [self sum:x y:y];
};
[self.myBlocks addObject:sum];
int(^area)(int, int) = ^(int x, int y) {
return [self area:x y:y];
};
[self.myBlocks addObject:area];
#pragma mark 调用保存在数组中的Block
int(^func)(int, int) = self.myBlocks[index];
return func(x, y);
Block的循环引用
局部变量默认都是强引用的,离开其所在的作用域之后就会被释放
使用__weak关键字,可以将局部变量声明为弱引用
__weak DemoObj *weakSelf = self;
__weak typeof(self) weakSelf = self;
在Block中引用weakSelf,则Block不会再对self做强引用
int(^sum)(int, int) = ^(int x, int y) {
return [weakSelf sum:x y:y];
};
提示:
iOS5.0以上版本使用__weak
iOS5.0以下版本使用__unsafe_unretained