概述:
- 能够截取自动变量的匿名函数
- 指向函数的指针
- 结构体
- oc对象
使用:
- 声明
格式:返回值类型(^变量名称)(参数列表)
int(^myBlock)(int a,int b)
void(^myBlock)(void)
- 定义(变量赋值)
格式:变量 = ^(参数列表){函数体};
myBlock = ^(int a, int b){
return 1;
};
myBlock = ^(void){
};
- 调用
格式:变量(参数列表);
myBlock(1,2);
myBlock();
- 使用typedef定义Block类型
格式:typedef 返回值类型(^变量名称)(参数列表)
typedef void(^MyBlock)();
MyBlock myBlock = ^(){
};
- Block作为函数参数
int(^MyBlock)(int a, int b) = ^(int a, int b){ // 声明定义一个参数为a,b,返回值为int类型的block
return 3;
};
[self blockTest:MyBlock];
void(^MyBlcok)(void) = ^(){
};
[self blockTest1:MyBlcok];
- (void)blockTest:(int(^)(int a, int b))myBlock{
}
- (void)blockTest1:(void(^)())myBlock{
}
注意:为了简化block的声明,一般使用typedef声明block类型
- Block作为函数返回值
int(^MyBlock)(int a, int b) = [self blockTest];
- (int(^)(int a, int b))blockTest{
return ^(int a,int b){
return a+b;
};
}
block类型:
- 全局区:NSConcreteGlobalBlock
- 栈区:NSConcreteStackBlock
- 堆区:NSConcreteMallocBlock
- NSConcreteGlobalBlock
block定义在函数之外(和全局变量一个地方)
void (^globalBlock)() = ^{
NSLog(@"大家好我是NSConcreteGlobalBlock-->");
};
int main(int argc, char * argv[]) {
@autoreleasepool {
globalBlock();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
转换成c++代码:
struct __globalBlock_block_impl_0 {
struct __block_impl impl;
struct __globalBlock_block_desc_0* Desc;
__globalBlock_block_impl_0(void *fp, struct __globalBlock_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteGlobalBlock; <--- 看这,这里。。
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
注意:在block没有截取任何自动变量的时候,也是NSConcreteGlobalBlock的,但我在ARC环境下,查看c++源码,是NSConcreteStackBlock,具体原因请看唐巧大神的说明,自行验证。
- NSConcreteStackBlock
定义在函数内部的block
int main(int argc, char * argv[]) {
@autoreleasepool {
void(^StackBlock)() = ^(){
NSLog(@"大家好,我是StackBlock");
};
StackBlock();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
转换成c++代码:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
注意:NSConcreteMallocBlock无法直接创建,当执行了以下操作时,系统会自动从NSConcreteStackBlock copy到NSConcreteMallocBlock中:
1.调用Block的copy实例方法时
2.Block作为函数返回值返回时
3.将Block赋值给附有__strong修饰符id类型的类或Block类型成员变量时
4.将方法名中含有usingBlock的Cocoa框架方法或GCD的API中传递Block时
截取自动变量:
- 局部变量
- 局部变量在block中不可修改。
- 局部变量是值传递,所以在block外面修改不影响block里截取的变量值。
oc代码:
int main(int argc, char * argv[]) {
@autoreleasepool {
int i = 0;
void(^MyBlock)() = ^(){
i++; // 报错
NSLog(@"大家好,我是MyBlock-->%d",i); // 注释掉上面的报错代码,输出的还是0;
};
i++;
MyBlock();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
c++代码:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int i;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int flags=0) : i(_i) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int i = __cself->i; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_k__jbgrnnkx4ds0dh6rz0rglp540000gn_T_main_658820_mi_1,i);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int i = 0;
void(*MyBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, i));
((void (*)(__block_impl *))((__block_impl *)MyBlock)->FuncPtr)((__block_impl *)MyBlock);
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
}
- 静态局部变量
- 局部静态变量在block中可修改。
- 局部静态变量是地址传递,所以在block外面修改会影响block里截取的变量值。
oc代码:
int main(int argc, char * argv[]) {
@autoreleasepool {
static int i = 0;
void(^MyBlock)() = ^(){
i++; // 可以修改,不会报错
NSLog(@"大家好,我是MyBlock-->%d",i);
};
i++;
MyBlock();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
c++代码:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int *i;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_i, int flags=0) : i(_i) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int *i = __cself->i; // bound by copy
(*i)++;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_k__jbgrnnkx4ds0dh6rz0rglp540000gn_T_main_e314e4_mi_0,(*i));
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
static int i = 0;
void(*MyBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &i));
i++;
((void (*)(__block_impl *))((__block_impl *)MyBlock)->FuncPtr)((__block_impl *)MyBlock);
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
}
- __block修饰局部变量(只能用于修饰普通局部变量,static,全局都不行)
- __block修饰的局部变量在block中可修改。
- __block修饰的局部变量,注意看下面c++源码,系统会把用__block修饰的变量包装成一个__Block_byref_i_0结构体对象。main_block_impl_0 中引用的是 Block_byref_i_0 的结构体指针,这样就可以达到修改外部变量的作用,因为传递的是该结构体的地址,所以我们在外面修改变量,会影响block中的值。
oc代码:
int main(int argc, char * argv[]) {
@autoreleasepool {
__block int i = 0;
void(^MyBlock)() = ^(){
i++; // 可以修改,不会报错
NSLog(@"大家好,我是MyBlock-->%d",i);
};
i++;
MyBlock();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
c++代码:
struct __Block_byref_i_0 { // 当用__block来修饰变量时,系统会吧变量包装成一个结构体对象。
void *__isa;
__Block_byref_i_0 *__forwarding;
int __flags;
int __size;
int i;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_i_0 *i; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_i_0 *i = __cself->i; // bound by ref
(i->__forwarding->i)++;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_k__jbgrnnkx4ds0dh6rz0rglp540000gn_T_main_be9fd2_mi_0,(i->__forwarding->i));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->i, (void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 0};
void(*MyBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344));
(i.__forwarding->i)++;
((void (*)(__block_impl *))((__block_impl *)MyBlock)->FuncPtr)((__block_impl *)MyBlock);
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
}
- 全局变量
- 全局变量在block中可修改。
- 全局变量存储在静态数据区,在程序销毁前不会销毁,所以在block中可以直接访问,因为访问的是一份地址,所以在外面修改会影响block里面变量的值。
oc代码:
int i = 0;
int main(int argc, char * argv[]) {
@autoreleasepool {
void(^MyBlock)() = ^(){
i++; // 可以修改,不会报错
NSLog(@"大家好,我是MyBlock-->%d",i);
};
i++;
MyBlock();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
c++代码:
int i = 0;
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
i++;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_k__jbgrnnkx4ds0dh6rz0rglp540000gn_T_main_ba21a2_mi_0,i);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
void(*MyBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
i++;
((void (*)(__block_impl *))((__block_impl *)MyBlock)->FuncPtr)((__block_impl *)MyBlock);
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
}
- 静态全局变量
- 静态全局变量在block中可修改。
- 静态全局变量存储在静态数据区,在程序销毁前不会销毁,所以在block中可以直接访问,因为访问的是一份地址,所以在外面修改会影响block里面变量的值。
oc代码:
static int i = 0;
int main(int argc, char * argv[]) {
@autoreleasepool {
void(^MyBlock)() = ^(){
i++; // 可以修改,不会报错
NSLog(@"大家好,我是MyBlock-->%d",i);
};
i++;
MyBlock();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
c++代码:
static int i = 0;
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
i++;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_k__jbgrnnkx4ds0dh6rz0rglp540000gn_T_main_e20576_mi_0,i);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
void(*MyBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
i++;
((void (*)(__block_impl *))((__block_impl *)MyBlock)->FuncPtr)((__block_impl *)MyBlock);
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
}
内存管理:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
int i = 0;
MyBlock = ^(){
NSLog(@"我是MyBlock--->%d",i);
};
MyBlock();
NSLog(@"myBlock--->%@",MyBlock);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
MyBlock();
}
- MRC
引用了局部变量i,所以block在NSStackBlock中,出了viewDidLoad方法,MyBlock会自动销毁,touchesBegan再调用MyBlock就会报野指针错误。
需要手动调用Block_copy方法把block复制到NSConcreteMallocBlock中,记得调用Block_release方法使计数器-1。 - ARC
会自动调用copy方法,把block复制到NSConcreteMallocBlock中。
注意:在Block中无论是MRC/ARC,没有调用局部变量时,都是NSConcreteGlobalBlock类型的,也就不会出现block销毁了,在调用的情况了。
循环引用:
- 调用系统的block不会循环引用,self并不持有该方法。
[UIView animateWithDuration:0.5 animations:^{
NSLog(@"%@", self);
}];
- 当self持有block时,block中又调用了self,会循环引用。
@property (nonatomic, copy)MyBlock myBlock;
self.myBlock = ^(){
self.xxxx;
};
// 不会循环引用
__weak typeof(self)weakSelf = self;
self.myBlock = ^(){
weakSelf.xxxx;
};
- 自定义对象中有个block属性,在调用该对象的block属性中,调用该对象的其他属性,会循环引用。
typedef void(^MyBlock)();
@interface MyBlockModel : NSObject
@property (nonatomic, copy) MyBlock myBlock;
@property (nonatomic, copy) NSString *text;
@end
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
MyBlockModel *model = [[MyBlockModel alloc] init];
model.myBlock = ^(){
NSLog(@"--->%@",model.text);
};
// 不会循环引用
MyBlockModel *model1 = [[MyBlockModel alloc] init];
__weak MyBlockModel *weakModel = model1;
model1.myBlock = ^(){
NSLog(@"--->%@",weakModel.text);
};
}
- 注意:在异步(多线程)环境下。防止self被释放,应该在block内部用强引用引用该弱引用。
__weak typeof(self)weakSelf = self;
self.myBlock = ^(){
__strong typeof (weakSelf)strongSelf = weakSelf;
if(strongSelf){
strongSelf.xxxx;
}
};
参考:
http://www.jianshu.com/p/51d04b7639f1
http://www.cocoachina.com/ios/20161025/17198.html
http://blog.devtang.com/2013/07/28/a-look-inside-blocks/
小提示:
1.如和把oc代码转成c++代码:
通过终端cd到你要编译的文件所在目录,输入:clang -rewrite-objc xxxx.m即可。(xxxx:你要编译的文件名称)。
2.如果出现main.m:9:9: fatal error: 'UIKit/UIKit.h' file not found怎么办?
clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode8.3.3.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk xxxx.m(xxxx:你要编译的文件名称)。