block本质上也是一个OC对象,封装了函数调用以及函数调用环境,block内部也有个isa指针。
block的类型
既然block是OC对象,那它属于什么类型呢?
测试打印:一个block的类 及父类
void (^block)(void) = ^{
NSLog(@"123");
};
NSLog(@"block.class = %@",[block class]);
NSLog(@"block.class.superclass = %@",[[block class] superclass]);
NSLog(@"block.class.superclass.superclass = %@",[[[block class] superclass] superclass]);
NSLog(@"block.class.superclass.superclass.superclass = %@",[[[[block class] superclass] superclass] superclass]);
// ----- 打印的结果:
// block.class = __NSGlobalBlock__
// block.class.superclass = __NSGlobalBlock
// block.class.superclass.superclass = NSBlock
// block.class.superclass.superclass.superclass = NSObject
上述例子中的 block的类型是__NSGlobalBlock
(全局的)
继承关系为__NSGlobalBlock__ : __NSGlobalBlock : NSBlock : NSObject
而下面这个block的类型是__NSStackBlock__
(栈空间的), 因为访问了auto变量。注意,这是MRC环境下看到的情况。
auto int a = 10;
void (^block)(void) = ^{
NSLog(@"a = %d",a);
};
而在ARC环境下,编译器会根据情况把block自动进行copy
操作(将栈上的block复制到堆上),此时block的类型变成了__NSMallocBlock__
(堆空间的)
- 结论:block共有3种类型(都继承自
NSBlock
)
__NSGlobalBlock__ ( _NSConcreteGlobalBlock )
__NSStackBlock__ ( _NSConcreteStackBlock )
__NSMallocBlock__ ( _NSConcreteMallocBlock )
-
MRC下block属性的建议用copy
修饰。
ARC下block属性可以用strong
也行,因为会自动copy操作。
无论是MAC还是ARC:
当block为__NSStackBlock__
类型时(在栈空间),无论对外面使用的是strong
还是weak
都不会对外面的对象进行强引用。
因为block如果在栈上,自己都随时可能消失,怎么能保住别人?
当block为__NSMallocBlock__
类型时(在堆空间),block内部会根据strong
或者weak
对外界的对象进行强引用或弱引用。
block的底层结构
int age = 20;
void (^block)(void) = ^{
NSLog(@"age is %d",age);
};
block();
将上面的代码转成C++代码:
int age = 20;
void (*block)(void) = &__main_block_impl_0(
__main_block_func_0,
&__main_block_desc_0_DATA,
age
);
// block的调用
block->FuncPtr(block);
看出,block的本质就是一个结构体对象__main_block_impl_0
。
结构体代码如下:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age;
//构造函数(类似于OC中的init方法) _age是外面传入的
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
//isa指向_NSConcreteStackBlock 说明这个block就是_NSConcreteStackBlock类型的
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
结构体中第一个是struct __block_impl impl;
如下:
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
结构体中第二个是__main_block_desc_0;
如下:
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size; // 结构体__main_block_impl_0 占用的内存大小
}
这里,如果block进行了copy操作(从栈空间变成堆空间)会增加copy和dispose。如下:
-
当block从堆中移除时,会调用block内部的dispose
函数,在dispose
内部释放捕获的变量。
结构体中第三个是age
,也就是捕获的局部变量。
下面的函数,封装了block执行逻辑 :
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int age = __cself->age; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_x4_920c4yq936b63mvtj4wmb32m0000gn_T_main_7f3f1b_mi_0,age);
}
网友评论