block概述
oc类是对数据和相关行为的封装,block是单一任务或行为的封装
- block属于oc对象,因此block可被添加到容器中
- block可捕获外围作用域对象,这类似于其他语言的闭包或lambda
block语法
可定义block类型对象及使用block字面值
void block()
{
void (^feed0)(void) = ^ void (void)
{
NSLog(@"feed nothing");
};
void (^feed1)(int) = ^ void (int rice)
{
NSLog(@"feed rice");
};
void (^feed2)(int, int) = ^ void (int rice, int meat)
{
NSLog(@"feed rice and meat");
};
feed0();
feed1(5);
feed2(5, 8);
}
注意:
- 左边为block对象定义,block类型形似函数指针类型
- 右边为block字面值,形似函数对象定义(缺少函数名)
- block字面值返回值类型可从block字面值中推导出来,因此可omit
- block字面值若无参,参数类型说明可omit
- block对象若为nil,调用会导致crash,这与oc实例对象不同,oc实例对象为nil,调用不会crash
因此下面代码与上面代码等价
void block()
{
void (^feed0)(void) = ^
{
NSLog(@"feed nothing");
};
void (^feed1)(int) = ^ (int rice)
{
NSLog(@"feed rice");
};
void (^feed2)(int, int) = ^ (int rice, int meat)
{
NSLog(@"feed rice and meat");
};
feed0();
feed1(5);
feed2(5, 8);
}
函数指针类型可typedef,block类型也可typedef
typedef void (^Feed0)(void);
typedef void (^Feed1)(int);
typedef void (^Feed2)(int, int);
void block()
{
Feed0 feed0 = ^
{
NSLog(@"feed nothing");
};
Feed1 feed1 = ^ (int rice)
{
NSLog(@"feed rice");
};
Feed2 feed2 = ^ (int rice, int meat)
{
NSLog(@"feed rice and meat");
};
feed0();
feed1(5);
feed2(5, 8);
}
函数指针类型可作为函数或方法参数,block类型也可作为函数或方法参数
typedef void (^Feed0)(void);
typedef void (^Feed1)(int);
typedef void (^Feed2)(int, int);
void blockArg1(Feed0 feed0)
{
feed0();
}
void blockArg2(Feed1 feed1, Feed2 feed2)
{
feed1(5);
feed2(5, 8);
}
void block()
{
blockArg1(^
{
NSLog(@"feed nothing");
});
blockArg2(^ (int rice)
{
NSLog(@"feed rice");
},
^ (int rice, int meat)
{
NSLog(@"feed rice and meat");
});
}
@interface FBAnimal : NSObject
- (void)blockArg1: (Feed0)feed0;
- (void)blockArg2: (Feed1)feed1 andFeed2: (Feed2)feed2;
- (void)block;
@end
@implementation FBAnimal
- (void)blockArg1: (Feed0)feed0
{
feed0();
}
- (void)blockArg2: (Feed1)feed1 andFeed2: (Feed2)feed2
{
feed1(5);
feed2(5, 8);
}
- (void)block
{
[self blockArg1:^
{
NSLog(@"feed nothing");
}];
[self blockArg2:^ (int rice)
{
NSLog(@"feed rice");
} andFeed2:^ (int rice, int meat)
{
NSLog(@"feed rice and meat");
}];
}
@end
注意:尽管函数或方法并没有限制block类型参数个数,但在实践中,建议至多使用一个block类型参数,如果函数或方法包含多个参数,出于代码可读性考虑,建议把block类型参数放在最后,这样直接使用block字面值传递实参值,代码可读性会好很多
capture value
无__block
@interface FBAnimal : NSObject
@end
@implementation FBAnimal
@end
void block()
{
int food = 5;
int* pFood = &food;
FBAnimal *animal = [[FBAnimal alloc] init];
NSLog(@"start: food addr = %p, pFood addr = %p, animal addr = %p", &food, &pFood, &animal);
NSLog(@"start: food = %d, pFood = %p, animal = %p", food, pFood, animal);
void (^captureBlock)(void) = ^
{
NSLog(@"block start: food addr = %p, pFood addr = %p, animal addr = %p", &food, &pFood, &animal);
NSLog(@"block start: food = %d, pFood = %p, animal = %p", food, pFood, animal);
//food = 15;
//pFood = NULL;
//animal = [[FBAnimal alloc] init];
NSLog(@"block end: food addr = %p, pFood addr = %p, animal addr = %p", &food, &pFood, &animal);
NSLog(@"block end: food = %d, pFood = %p, animal = %p", food, pFood, animal);
};
food = 25;
pFood = NULL;
animal = [[FBAnimal alloc] init];
NSLog(@"end: food addr = %p, pFood addr = %p, animal addr = %p", &food, &pFood, &animal);
NSLog(@"end: food = %d, pFood = %p, animal = %p", food, pFood, animal);
captureBlock();
}
output:
start: food addr = 0x7fff50aab96c, pFood addr = 0x7fff50aab960, animal addr = 0x7fff50aab958
start: food = 5, pFood = 0x7fff50aab96c, animal = 0x7f84734aa180
end: food addr = 0x7fff50aab96c, pFood addr = 0x7fff50aab960, animal addr = 0x7fff50aab958
end: food = 25, pFood = 0x0, animal = 0x7f84734ab140
block start: food addr = 0x7f8473412c30, pFood addr = 0x7f8473412c28, animal addr = 0x7f8473412c20
block start: food = 5, pFood = 0x7fff50aab96c, animal = 0x7f84734aa180
block end: food addr = 0x7f8473412c30, pFood addr = 0x7f8473412c28, animal addr = 0x7f8473412c20
block end: food = 5, pFood = 0x7fff50aab96c, animal = 0x7f84734aa180
结论:
- 定义block时,block内capture对象copy了block外对象,并非同一对象,且block内对象不可修改
含__block
@interface FBAnimal : NSObject
@end
@implementation FBAnimal
@end
void block()
{
__block int food = 5;
__block int* pFood = &food;
__block FBAnimal *animal = [[FBAnimal alloc] init];
NSLog(@"start: food addr = %p, pFood addr = %p, animal addr = %p", &food, &pFood, &animal);
NSLog(@"start: food = %d, pFood = %p, animal = %p", food, pFood, animal);
void (^captureBlock)(void) = ^
{
NSLog(@"block start: food addr = %p, pFood addr = %p, animal addr = %p", &food, &pFood, &animal);
NSLog(@"block start: food = %d, pFood = %p, animal = %p", food, pFood, animal);
food = 15;
pFood = NULL;
animal = [[FBAnimal alloc] init];
NSLog(@"block end: food addr = %p, pFood addr = %p, animal addr = %p", &food, &pFood, &animal);
NSLog(@"block end: food = %d, pFood = %p, animal = %p", food, pFood, animal);
};
food = 25;
pFood = NULL;
animal = [[FBAnimal alloc] init];
NSLog(@"end: food addr = %p, pFood addr = %p, animal addr = %p", &food, &pFood, &animal);
NSLog(@"end: food = %d, pFood = %p, animal = %p", food, pFood, animal);
captureBlock();
}
output:
start: food addr = 0x7fff53a37950, pFood addr = 0x7fff53a37930, animal addr = 0x7fff53a37910
start: food = 5, pFood = 0x7fff53a37950, animal = 0x7f83aadbe270
end: food addr = 0x7f83aadc1c58, pFood addr = 0x7f83aadba148, animal addr = 0x7f83aadc4768
end: food = 25, pFood = 0x0, animal = 0x7f83aadba150
block start: food addr = 0x7f83aadc1c58, pFood addr = 0x7f83aadba148, animal addr = 0x7f83aadc4768
block start: food = 25, pFood = 0x0, animal = 0x7f83aadba150
block end: food addr = 0x7f83aadc1c58, pFood addr = 0x7f83aadba148, animal addr = 0x7f83aadc4768
block end: food = 15, pFood = 0x0, animal = 0x7f83aadbe270
结论: