相信有开发经验的人都对block不陌生,它与c语言的block相似但是也有不同之处。
有兴趣的同学可以通过运行时机制看看oc的c语言代码,通过CLong有助于对底层构建的了解。如果你对函数的指针,指针的指针不了解还是回去复习一下吧...
复习过后有几点问题需要强调,面试的时候也可能会提到
- 首先是block的基本定义:
- Block是OC中的一种数据类型,在iOS开发中被广泛使用
- ^是Block的特有标记
- Block的实现代码包含在{}之间
- 大多情况下,以内联inline函数的方式被定义和使用
- Block与C语言的函数指针有些相似,但使用起来更加灵活
- 示例
void(^demoBlock)() =^ {
NSLog(@"demoBlock");
};
int(^sumBlock)(int, int) =^(int x, int y) {
return x +y;
};
- 格式说明:
( 返回类型 )(^ 块名称 )( 参数类型 ) = ^ ( 参数列表 ) { 代码实现 };如果没有参数,等号后面参数列表的 () 可以省略
注意:(1) 在定义 Block 时,会在 Block 中 建立当前局部变量内容的副本 ( 拷贝 )(2) 后续再对该变量的数值进行修改,不会影响 Block 中的数值(3) 如果需要在 block 中保持局部变量的数值变化,需要使用 __block 关键字(4)使用__block关键字后,同样可以在Block中修改该变量的数值
-
Block可以被当做参数直接传递
NSArray *array= @[@"张三",@"李四",@"王五",@"赵六"];
[array enumerateObjectsUsingBlock:^(id obj, NSUIntegeridx, BOOL*stop) {
NSLog(@"第%d 项内容是%@",(int)idx, obj);
if ([@"王五"isEqualToString:obj]) {
*stop = YES;
}
}];//遍历并NSLog()array中的内容,当obj 为"王五"时停止遍历
- 在被当做参数传递时, Block 同样可以使用 在定义之前声明 的 局部变量
intstopIndex = 1;
NSArray *array = @[@"张三", @"李四", @"王五", @"赵六"];
[arrayenumerateObjectsUsingBlock:^(id obj, NSUIntegeridx, BOOL *stop) {
NSLog(@"第%d 项内容是%@", (int)idx, obj);
if ([@"王五" isEqualToString:obj] || idx == stopIndex) {
*stop = YES;
}
}];
注意 , 默认情况下, Block 外部的变量,在 Block 中是只读的!BOOL flag = NO;
[arrayenumerateObjectsUsingBlock:^(id obj, NSUIntegeridx, BOOL *stop) {
if([@"王五" isEqualToString:obj] || idx == stopIndex) {
*stop = YES;
flag = YES; // 编译错误!!!
}
}];
- 如果要修改 Block 之外的局部变量,需要使用 __block 关键字
__block BOOL flag = NO;
[array enumerateObjectsUsingBlock:^(id obj, NSUIntegeridx, BOOL *stop) {
if([@"王五" isEqualToString:obj] || idx == stopIndex) {
*stop = YES;
flag = YES; // 现在可以修改了!!!
}
}];
提示: 无需使用 __block 关键字,在块代码中可以 修改成员变量的数值 ( 比较少用 ) -
对象传递进 Block 的方式
NSString *stopName =@"王五";
NSArray *array= @[@"张三",@"李四",@"王五",@"赵六"];
[array enumerateObjectsUsingBlock:^(idobj, NSUIntegeridx,BOOL *stop) {
NSLog(@"第%d 项内容是%@",(int)idx, obj);
if ([stopName isEqualToString:obj] || idx == stopIndex) {
*stop = YES;
}
}];为保证Block中的代码正常运行,在将stopName的指针传递给Block时,Block会自动对stopName的指针做强引用
-
typedef 定义一个 Block 的类型 , 便于在后续直接使用
typedef double(^MyBlock)(double, double);
MyBlockarea = ^(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));
说明:typedef 是关键字用于定义类型, MyBlock 是定义的 Block 类型area 、 sum 分别是 MyBlock 类型的两个 Block 变量 -
官方的数组遍历方法声明如下:
-(void)enumerateObjectsUsingBlock:(void(^)(id obj, NSUIntegeridx, BOOL*stop))block;
-
既然 Block 是 一种数据类型 , 那么可以将 Block 当做比较特殊的对象
int(^sum)(int, int) =^(int x, int y) {
return [self sum:xy:y];
};
[self.myBlocks addObject:sum];
- 循环引用-倘若你这样写代码:
@property(nonatomic, strong) NSMutableArray *myBlocks;
int(^sum)(int, int) = ^(int x, int y) {
return [self sum:xy:y];
};
[self.myBlocksaddObject:sum];
- (void)dealloc
{
NSLog(@"DemoObj被释放");
}
结果就是不会被释放 因为 self -> (NSMutableArray*)myBlocks -> sum -> self 死循环强指针,释放不了了 - 解除循环引用
局部变量默认都是强引用的 ,离开其所在的作用域之后就会被释放使用 __weak 关键字,可以将局部变量声明为弱引用
__weak DemoObj *weakSelf =self;
//在 Block 中引用 weakSelf ,则 Block 不会再对 self 做强引用int(^sum)(int, int) = ^(int x, int y) {
return [weakSelf sum:xy:y];
};
感谢传智播客提供的文档