block原始代码为:
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 10;
void (^block)() = ^{
printf("%d\n", a);
};
//再次给a 赋值
a = 20;
block();
}
return 0;
}
将上面block原始代码转换为C++代码: 通过 命令 :clang -rewrite-objc main.m
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int a;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int a = __cself->a; // bound by copy
printf("%d\n", a);
}
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, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int a = 10;
void (*block)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
a = 20;
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
block原始代码为:
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int a = 10;
void (^block)() = ^{
printf("%d\n", a);
};
//再次给a 赋值
a = 20;
block();
}
return 0;
}
将上面block原始代码转换为C++代码: 通过 命令 :clang -rewrite-objc main.m
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;
int __flags;
int __size;
int a;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_a_0 *a; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__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_a_0 *a = __cself->a; // bound by ref
printf("%d\n", (a->__forwarding->a));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->a, 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, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};
void (*block)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));//这里的570425344 最后赋值给 flags 但是 flags 直接又被赋为 0,这个570425344在此处没用。
(a.__forwarding->a) = 20;
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
类 | 设置对象的存储域 |
_NSConcreteStackBlock | 栈 |
_NSConcreteGlobalBlock | 程序的代码段(.text区) |
_NSConcreteMallocBlock | 堆 |
存储域
通过上面的描述我们知道Block和__block变量实质就是一个相应结构体的实例。我们在上述转换过的代码中可以发现 __main_block_impl_0 结构体构造函数中, isa指向的是 _NSConcreteStackBlock。Block还有另外两个与之相似的类:
_NSConcreteStackBlock 保存在栈中的block,出栈时会被销毁
_NSConcreteGlobalBlock 全局的静态block,不会访问任何外部变量
_NSConcreteMallocBlock 保存在堆中的block,当引用计数为0时会被销毁
上述示例代码中,Block是被设为_NSConcreteStackBlock,在栈上生成。当我们把Block作为全局变量使用时,对应生成的Block将被设为_NSConcreteGlobalBlock,如:
1
2
3
4
5
6
7
|
void (^block)(void) = ^{NSLog(@
"This is a Global Block"
);};
int main(int argc, const char * argv[]) {
@autoreleasepool {
block();
}
return
0;
}
|
该代码转换后的代码中,Block结构体的成员变量isa的初始化如下:
1
|
impl.isa = &_NSConcreteGlobalBlock;
|
那么_NSConcreteMallocBlock在什么时候被使用呢?
分配在全局变量上的Block,在变量作用域外也可以通过指针安全的访问。但分配在栈上的Block,如果它所属的变量作用域结束,该Block就被废弃。同样地,__block变量也分配在栈上,当超过该变量的作用域时,该__block变量也会被废弃。
这个时候_NSConcreteMallocBlock就登场了,Blocks提供了将Block和__block变量从栈上复制到堆上的方法来解决这个问题。将分配到栈上的Block复制到堆上,这样但栈上的Block超过它原本作用域时,堆上的Block还可以继续存在。
复制到堆上的Block,它的结构体成员变量isa将变为:
1
|
impl.isa = &_NSConcreteMallocBlock;
|
而_block变量中结构体成员__forwarding就在此时保证了从栈上复制到堆上能够正确访问__block变量。在这种情况下,只要栈上的_block变量的成员变量__forwarding指向堆上的实例,我们就能够正确访问。
我们一般可以使用copy方法手动将 Block 或者 __block变量从栈复制到堆上。比如我们把Block做为类的属性访问时,我们一般把该属性设为copy。有些情况下我们可以不用手动复制,比如Cocoa框架中使用含有usingBlock方法名的方法时,或者GCD的API中传递Block时。
当一个Block被复制到堆上时,与之相关的__block变量也会被复制到堆上,此时堆上的Block持有相应堆上的__block变量。当堆上的__block变量没有持有者时,它才会被废弃。(这里的思考方式和objc引用计数内存管理完全相同。)
//
// main.m
// test_block_05
//
// Created by admin on 4/28/16.
// Copyright © 2016 jeffasd. All rights reserved.
//
#import
void (^globalBlock)() = ^(){
NSLog(@"fsdfsdf");
return;
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
int foo = 3;
NSLog(@"the foo address is %p", &foo);
int foo1 = 4;
NSLog(@"the foo1 address is %p", &foo1);
int foo2 = 5;
NSLog(@"the foo2 address is %p", &foo2);
__block int a = 10;
// int a = 10;
NSLog(@"11 %p", &a); //0x7fff5fbff7f8 地址很大在栈中
void (^block)() = ^{
NSLog(@"block a address %p", &a); //0x7fff5fbff7f8 地址很小在堆中
printf("%d\n", a);
};
//再次给a 赋值
NSLog(@"11 block is %@", block);
NSLog(@"%p", &a);
a = 20;
block();
NSLog(@"block is %@", block);
NSLog(@"the global block is %@", globalBlock);
globalBlock();
NSLog(@"the global block is %@", globalBlock);
__weak void (^weakBlock)() = ^(){
NSLog(@"fsdfsdf");
return;
};
NSLog(@"the weak block is %@", weakBlock);
}
return 0;
}
//int main(int argc, const char * argv[]) {
// /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
//
// int a = 10;
//
// void (*block)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
//
//
//
// a = 20;
//
// ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
// }
// return 0;
//}
//static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
//切换成C++语音
//int main(int argc, const char * argv[]) {
// @autoreleasepool {
//
// __block int a = 10;
//
// void (^block)() = ^{
// printf("%d\n", a);
// };
//
// //再次给a 赋值
//
// a = 20;
//
// block();
// }
// return 0;
//}
////切换成C++语音
//int main(int argc, const char * argv[]) {
// /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
//
// __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};
//
// void (*block)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
//
//
//
// (a.__forwarding->a) = 20;
//
// ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
// }
// return 0;
//}
//static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
这里还有一点关于block类型的ARC属性。上文也说明了,ARC会自动帮strong类型
且捕获外部变量
的block进行copy,所以在定义block类型的属性时也可以使用strong,不一定使用copy。也就是以下代码:
/** 假如有栈block赋给以下两个属性 **/
// 这里因为ARC,当栈block中会捕获外部变量时,这个block会被copy进堆中
// 如果没有捕获外部变量,这个block会变为全局类型
// 不管怎么样,它都脱离了栈生命周期的约束
@property (strong, nonatomic) Block *strongBlock;
// 这里都会被copy进堆中
@property (copy, nonatomic) Block *copyBlock;