main.m
中的代码
int main(int argc, const char * argv[]) {
int age = 18;
static int height = 55;
void (^block)(int) = ^(int pa) {
NSLog(@"--block-局部auto变量的捕获--%d",age);
NSLog(@"--block-局部static变量的捕获--%d",height);
};
NSLog(@"---%@",[block class]);
NSLog(@"---%@",[[block class] superclass]);
NSLog(@"---%@",[[[block class] superclass] superclass]);
NSLog(@"---%@",[[[[block class] superclass] superclass] superclass]);
block(10);
}
打印可见block也是一个oc对象
2019-06-08 11:38:23.353777+0800 Tes[41864:3485021] —NSStackBlock
2019-06-08 11:38:23.354053+0800 Tes[41864:3485021] —__NSStackBlock
2019-06-08 11:38:23.354091+0800 Tes[41864:3485021] —NSBlock
2019-06-08 11:38:23.354134+0800 Tes[41864:3485021] —NSObject
2019-06-08 11:38:23.354275+0800 Tes[41864:3485021] --block-局部auto变量的捕获–18
2019-06-08 11:38:23.354314+0800 Tes[41864:3485021] --block-局部static变量的捕获–56
将以上代码通过xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
转化为c++代码,可以看到block
的结构
//block的结构体
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age;
int *height;
//构造函数
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int *_height, int flags=0) : age(_age), height(_height) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
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)};
blcok的实现作为第一个参数传过去,捕获的变量在后边作为参数,调用__main_block_impl_0
结构体的构造函数,实现block的创建
int main(int argc, const char * argv[]) {
int age = 18;
static int height = 55;
void (*block)(int) = ((void (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age, &height));
//把上边转化的代码去掉强制转换
void (*block)(int) = &__main_block_impl_0(
__main_block_func_0,
&__main_block_desc_0_DATA,
age,
&height);
}
block的具体实现被封装为的函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int pa) {
int age = __cself->age; // bound by copy
int *height = __cself->height; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_pg_vnxk1kl519z1ks1hddc4mr0h0000gn_T_main_53cf21_mi_0,age);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_pg_vnxk1kl519z1ks1hddc4mr0h0000gn_T_main_53cf21_mi_1,(*height));
}
block的调用:block(10);
因为block
是__main_block_impl_0
类型,所以通过取出__block_impl
类型的imp
,然后再通过imp
取出FuncPtr
进行调用.但是这里为什么是直接把blcok
强制转为__block_impl
类型?因为impl
作为__main_block_impl_0
结构体的首元素,而__main_block_impl_0
变量的地址就是它的首元素的地址,所以这里强制转换得以实现
((void (*)(__block_impl *, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 10);
//把上边转化的代码去掉强制转换
(block->FuncPtr)(block, 10);
//理论上应该转为
(block->imp->FuncPtr)(block, 10);
__main_block_impl_0
结构体,看到多了个int age和int *height
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age;
int *height;
}
//生成的函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int pa) {
//使用的时候,会从本block变量内部获取
int age = __cself->age; // bound by copy
int *height = __cself->height; // bound by copy
}
用
auto
修饰的局部变量(局部变量,默认就是auto): 因为它是局部变量,会自动销毁,而如果在block调用前就销毁了,那么block调用时,获取到的值就会出问题,所以实行值传递.
用
static
修饰的局部变量: 虽然它也是局部变量,但是因为被static
修饰,所以出了变量作用域,不会自动销毁,那么block调用时,依然可以获取到正确的值,所以指针传递不会造成问题.
int age_ = 18;
static int height_ = 55;
int main(int argc, const char * argv[]) {
void (^block)(int) = ^(int pa) {
NSLog(@"--block-局部auto变量的捕获--%d",age_);
NSLog(@"--block-局部static变量的捕获--%d",height_);
};
NSLog(@"---%@",[block class]);
NSLog(@"---%@",[[block class] superclass]);
NSLog(@"---%@",[[[block class] superclass] superclass]);
NSLog(@"---%@",[[[[block class] superclass] superclass] superclass]);
block(10);
}
转为c++查看blcok
的结构体,发现并没有age_
和height_
,因为他们是全局变量,随时,哪里都可以访问到,所以没必要捕获
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
}
//生成的函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int pa) {
//使用的时候,直接调用全局变量
NSLog((NSString *)&__NSConstantStringImpl__var_folders_pg_vnxk1kl519z1ks1hddc4mr0h0000gn_T_main_ccc8d0_mi_2,age_);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_pg_vnxk1kl519z1ks1hddc4mr0h0000gn_T_main_ccc8d0_mi_3,height_);
}
全局变量不会捕获
int main(int argc, const char * argv[]) {
int age = 18;
//__NSStackBlock__
NSLog(@"----%@",[^(int pa) {
NSLog(@"--block---%d",age);
} class]);
//__NSGlobalBlock__
void (^block)(int) = ^(int pa) {
NSLog(@"---");
};
NSLog(@"----%@",[block class]);
//__NSMallocBlock__
void (^block1)(int) = ^(int pa) {
NSLog(@"--block---%d",age);
};
NSLog(@"----%@",[block1 class]);
}
打印:
2019-06-08 11:51:08.351813+0800 Tes[42036:3500881] ----NSStackBlock
2019-06-08 11:51:08.352169+0800 Tes[42036:3500881] ----NSGlobalBlock
2019-06-08 11:51:08.352201+0800 Tes[42036:3500881] ----NSMallocBlock
内存中从地地址到高地址:
NSGlobalBlock: 没有访问auto类型的局部变量(是GlobalBlock),存放在数据段
NSMallocBlock: (是MallocBlock),存放在堆里
NSStackBlock : 访问auto类型的局部变量,存放在栈里
typedef void(^RTBlock)(int);
int main(int argc, const char * argv[]) {
RTBlock block;
{
RTPerson *person = [[RTPerson alloc] init];
NSLog(@"--block---%@",[^(int pa) {
NSLog(@"--block---%@",person);
} class]);
}
NSLog(@"--block---%@",[block class]);
}
打印:
2019-06-10 13:04:08.687481+0800 Tes[42877:3569172] --block—NSStackBlock
2019-06-10 13:04:08.687816+0800 Tes[42877:3569172] dealloc
2019-06-10 13:04:08.687839+0800 Tes[42877:3569172] --block—(null)
因为block
的类型是stack
所以并不会对person
进行强引用
int main(int argc, const char * argv[]) {
RTPerson *person = [[RTPerson alloc] init];
//__NSMallocBlock__
void (^block1)(int) = ^(int pa) {
NSLog(@"--block---%@",person);
};
NSLog(@"----%@",[block1 class]);
}
转为c++代码后发现,__main_block_desc_0
结构体中多了两个函数指针
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};
__main_block_copy_0,这个函数会根据这个block捕获的oc对象的类型是弱引用还是强引用是否来对它进行强引用
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->person, (void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
__main_block_dispose_0,这个函数用来释放强引用
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
typedef void(^RTBlock)(int);
int main(int argc, const char * argv[]) {
__block int age = 18;
RTBlock block = ^(int pa) {
NSLog(@"--block---%d",age);
age = pa;
NSLog(@"--block---%d",age);
};
block(1);
}
转为c++代码后发现和以前的结构有所区别,以前的变量类型就是变量的本身类型,age变成了__Block_byref_age_0
类型
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_age_0 *age; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
而__Block_byref_age_0
结构体的代码为
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
__block int age = 18;
转为c++代码为:
__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 18};
//去掉强转
__Block_byref_age_0 age = {
0,
&age, //__forwarding 指针指向自己
0,
sizeof(__Block_byref_age_0),
18 //数据
};
block的创建的时候把上边生成的__Block_byref_age_0类型的age
的地址赋值给自己内部的age
RTBlock block = (
(void (*)(int))&__main_block_impl_0((void *)__main_block_func_0,
&__main_block_desc_0_DATA,
(__Block_byref_age_0 *)&age,
570425344));
__main_block_impl_0
函数内部修改age的值,然后就实现修改age
的值了
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int pa) {
__Block_byref_age_0 *age = __cself->age; // bound by ref
...
(age->__forwarding->age) = pa;
...
}
但是为什么修改__Block_byref_age_0
里的age
的值,外边的值也变化了呢,它们的地址一样?下面通过打印地址证明
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*);
// 这两个函数是内存管理相关的,这里参数先写为void,不影响使用
void (*copy)(void);
void (*dispose)(void);
};
struct __Block_byref_age_0 {
void *__isa;
struct __Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
struct __Block_byref_age_0 *age; // by ref
};
typedef void(^RTBlock)(int);
int main(int argc, const char * argv[]) {
__block int age = 18;
RTBlock block = ^(int pa) {
NSLog(@"--block---%d",age);
age = pa;
NSLog(@"--block---%d",age);
};
struct __main_block_impl_0 *s_block = (__bridge struct __main_block_impl_0 *) block;
NSLog(@"--外部的age地址---%p",&age);
NSLog(@"--结构体内部的age地址---%p",&s_block->age->age);
}
打印结果:
2019-06-08 14:30:48.880799+0800 Tes[43886:3665093] --外部的age地址—0x10180cd28
2019-06-08 14:30:48.881041+0800 Tes[43886:3665093] --结构体内部的age地址—0x10180cd28
__block修饰
,__main_block_desc_0
结构体中也出现了copy
和dispose
指针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};
所以会把_block
的__Block_byref_age_0 *age
这个结构体指针复制到堆上去
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}
__Block_byref_id_object_copy
和__Block_byref_id_object_dispose
,对OC对象进行内存管理struct __Block_byref_person_0 {
void *__isa;
__Block_byref_person_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
RTPerson *person;
};
__Block_byref_id_object_copy_131,这个函数会根据这个block捕获的oc对象的类型是弱引用还是强引用是否来对它进行强引用
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
_Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
/*__isa: 8字节,
__forwarding: 8字节,
int __flags: 4字节,
int __size: 4字节,
__Block_byref_id_object_copy: 8字节
__Block_byref_id_object_dispose: 8字节
总共40字节
所以dst + 40 就是person的地址
*/
}
__Block_byref_id_object_dispose_131,这个函数用来释放强引用
static void __Block_byref_id_object_dispose_131(void *src) {
_Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}
例子:
没使用weak
修饰
RTBlock block;
{
__block RTPerson *person = [[RTPerson alloc] init];
block = ^(int pa) {
NSLog(@"--block---%@",person);
};
}
NSLog(@"--block类型---%@",[block class]);
打印:
2019-06-08 14:12:13.198105+0800 Tes[45184:3784877] --block类型—NSMallocBlock
2019-06-08 14:12:17.398071+0800 Tes[45184:3784877] RTPerson dealloc
使用weak
修饰
RTBlock block;
{
RTPerson *person = [[RTPerson alloc] init];
__block __weak RTPerson *weakP = person;
block = ^(int pa) {
NSLog(@"--block---%@",weakP);
};
}
NSLog(@"--block类型---%@",[block class]);
打印:
2019-06-08 14:13:22.198105+0800 Tes[45184:3784877] RTPerson dealloc
2019-06-08 14:13:26.398071+0800 Tes[45184:3784877] --block类型—NSMallocBlock
注意:
当MRC时
__Block_byref_id_object_copy_131
并不会对OC对象进行强引用!!!
RTBlock block;
{
__block RTPerson *person = [[RTPerson alloc] init];
block = [^(int pa) {
NSLog(@"--block---%@",person);
} copy] ;
[person release];
}
NSLog(@"--block类型---%@",[block class]);
打印:
2019-06-08 14:17:43.198105+0800 Tes[45184:3784877] RTPerson dealloc
2019-06-08 14:17:47.398071+0800 Tes[45184:3784877] --block类型—NSMallocBlock