# block 的底层实现
将main.m中的代码通过clang编译成main.cpp代码:
int main(int argc, const char * argv[]) {
// insert code here...
void (^blk)(void) = ^{ printf("FlyElephant---Block\n"); };
blk();
return 0;
}
block通过__main_block_impl_0初始化,后序工作通过 __block_impl ,__main_block_desc_0实现。
## __block_impl
__block_impl结构体代码如下:
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
isa 指向实例对象,block 本身也是一个 Objective-C 对象。block 的三种类型:_NSConcreteStackBlock、_NSConcreteGlobalBlock、_NSConcreteMallocBlock,即当代码执行时,isa 有三种取值 :
impl.isa = &_NSConcreteStackBlock;
impl.isa = &_NSConcreteMallocBlock;
impl.isa = &_NSConcreteGlobalBlock;
Flags 按位承载 block 的附加信息;
Reserved 保留变量;
FuncPtr 函数指针,指向 Block 要执行的函数
以上的四个字段均在初始化的时候完成:
__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;
}
其中FuncPtr指针指向的block函数:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("FlyElephant---Block\n"); }
## __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;
}
};
impl block 实现的结构体变量;
Desc 描述 block 的结构体变量;
__main_block_impl_0 结构体的构造函数,初始化结构体变量 impl、Desc;
## __main_block_desc_0
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)};
reserved 结构体信息保留字段
Block_size 结构体大小
整个实现过程就是初始化__main_block_impl_0,返回impl,执行impl->FuncPtr.
## block访问变量两种方式
1.静态变量,全局变量
block使用静态变量,全局变量,全局静态变量代码:
int global_val = 1;
static int static_global_val = 2;
int main(int argc, const char * argv[]) {
// insert code here...
static int static_val = 3;
void(^blk)(void) = ^ {
global_val = 2;
static_global_val = 3;
static_val = 4;
};
return 0;
}
对静态全局变量和全局变量访问和转换之前一样,对静态变量的方式是将指针保存了起来。block对于其自动变量而言没有将指针保存起来,是因为自动变量(局部变量)超出其作用域之后就会被废弃。
2.__block
通过__block看下代码:
__block int localValue = 0;
void (^blk)(void) = ^{
localValue = 1;
};
OC源码中的 __block localValue 翻译后变成了 __Block_byref_intValue_0 结构体指针变量 intValue,通过指针传递到 block 内,与静态变量的指针传递是一致的。
## block类型
block 有三种类型 NSConcreteGlobalBlock,NSConcreteStackBlock和NSConcreteMallocBlock。
## block 循环引用
如果在Block使用附有__strong修饰符的对象类型自动变量,那么当Block从栈复制到堆上时,该对象为Block所持有。
经典的循环引用是self与block之间的相互引用:
typedef void (^blk_t)(void);
@interface User()
{
blk_t blk_;
}
@end
@implementation User
-(id)init
{
self = [super init];
blk_ = ^{NSLog(@"self = %@", self);};
return self;
}
-(void)dealloc
{
NSLog(@"dealloc");
}
@end
## __weak
__weak typeof(User) *weakself = self;
blk_= ^{
NSLog(@"FlyElephant--%ld", (long)weakself.age);
};
return self;
## __block避免循环引用:
typedef void (^blk_t)(void);
@interface User : NSObject
{
blk_t blk_;
}
@end
@implementation User
-(id)init
{
self = [super init];
__block id tmp = self;
blk_= ^{
NSLog(@"self = %@", tmp);
tmp = nil;
};
return self;
}
-(void)execBlock
{
blk_();
}
-(void)dealloc
{
NSLog(@"dealloc");
}
@end
int main()
{
id object = [[User alloc] init];
[object execBlock];
return 0;
}
该代码没有引起循环引用。但是如果不执行execBlock实例方式,即不执行复试给成员变量blk_的Block,会循环引用并引起内存泄漏。