Block是什么:
我们可以认为是一段代码并可以将这段代码当做变量,通过block()的方式进行回调。这不免让我们想到在C函数中,我们可以定义一个指向函数的指针并且调用:
void (*task)(void);
void run(){
NSLog(@"run");
}
{
task = run;
(*task)();
}
上面的函数指针可以直接通过 (*task)()的方式调用run这个函数,这样对比block跟似乎C语言的函数指针是一样的,但是两者仍然存在以下区别:
- block的代码是内联的,效率高于函数调用
- block对于外部变量默认是只读属性
- block被Objective-C看成是对象处理
iOS Block定义
//直接定义
@property (nonatomic, copy) void(^block)(NSInteger);
//方法中定义
block:(void(^) (NSInteger index))block;
//其他定义
typedef void(^Block)(NSInteger index);
OC中Block是作为对象存在的,包含3个对象:
- GlobalBlock 位于全局区,在Block内部不能使用外部变量
- MallocBlock 位于堆区,在Block内部使用局部变量或OC属性,并且赋值给强引用或者copy修饰的变量
- StackBlock 位于栈区,与MallocBlock一样,可以在内部使用局部变量或者OC属性。但是不能赋值给强引用或者copy修饰的变量。
例:
@interface Student : NSObject
@property (nonatomic, copy) void(^doWork)(void);
@end
Student *student = [Student new];
student.doWork = ^{
};
doWork这个block是什么类型,虽然有强引用,但是没有捕获变量,所以是GlobalBlock。
误区一block不是只能被copy修饰也可以用weak
@interface Student : NSObject
@property (nonatomic, weak) void(^doWork)(void);
@end
Student *student = [Student new];
student.doWork = ^{
};
下面这个block是什么类型?
@interface Student : NSObject
@property (nonatomic, weak) void(^doWork)(void);
@end
Student *student = [Student new];
int a;
student.doWork = ^{
a;
};
这个block没有被强引用,但是有捕获局部变量,所以是StackBlock.
我们稍微改下代码用copy修饰
@interface Student : NSObject
@property (nonatomic, copy) void(^doWork)(void);
@end
Student *student = [Student new];
int a;
student.doWork = ^{
a;
};
那么这个就是一个MallocBlock;
认识了Block的几种类型,我们看下Block底层是怎么实现,block的底层结构如图所示
为了保证block内部能够正常访问外部变量,block有个变量捕获机制
每一个类型的block调用了copy的结果
对于auto变量的处理
当block内部访问了对象类型的auto变量时
如果block是在栈上,将不会对auto变量产生强引用如果block被拷贝到堆上会调用block内部的copy函数copy函数内部会调用_Block_object_assign函数_Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用
-
如果block从堆上移除会调用block内部的dispose函数dispose函数内部会调用_Block_object_dispose函数_Block_object_dispose函数会自动释放引用的auto变量(release)
如果想修改auto变量值可以通过__block修饰不能修饰全局变量、静态变量(static),底层实现上就是编译器会将__block变量包装成一个对象,然后就可以更改外部的值。
当block在栈上时,并不会对__block变量产生强引用
当block被copy到堆时会调用block内部的copy函数copy函数内部会调用_Block_object_assign函数_Block_object_assign函数会对__block变量形成强引用(retain)
如果__block变量从堆上移除会调用__block变量内部的dispose函数
dispose函数内部会调用_Block_object_dispose函数_Block_object_dispose函数会自动释放指向的对象(release)