关于block捕获外部的变量
(1)局部变量
(2)静态变量
(3)全局变量
(4)全局静态变量
(6)block捕获外边的变量在里面可以更改变量的值吗?可更改是为什么,不可更改是为什么.
(5)block捕获基本类型变量和捕获对象类型变量的区别
关于block的类型
(1)堆block
(2)栈block
(3)全局block
(4)block在什么情况下分别是什么类型
关于block在MRC和在ARC注意事项
(1)block之后copy 分别在MRC, ARC会发生什么
(2)block会不会持有捕获的对象变量
关于循环引用__weak
(1)__weak对象或者__strong对象,block是怎么对捕获的对象进行内存管理的
关于__block
(1)__block修饰后的变量本质是什么
(2)在block内部为什么不能修改外边的局部变量
我们先看看下面代码根据xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m命令转成的c++代码
int globle_a = 1;
static int globlea_static_a = 2;
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autob = 1;
auto int autoa = 1;
static int statica = 1;
void(^lc_block)(int , NSObject *) = ^(int a, NSObject *obj){
//参数
NSLog(@"%d",a);
NSLog(@"%@",obj);
//捕获的外部变量
NSLog(@"%d",autoa);
NSLog(@"%d",statica);
NSLog(@"%d",globle_a);
NSLog(@"%d",globlea_static_a);
};
lc_block(10,[NSObject new]);
}
return 0;
}
下面是main.cpp中关于block 的关键代码
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
int globle_a = 1;
static int globlea_static_a = 2;
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int autoa;
int *statica;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _autoa, int *_statica, int flags=0) : autoa(_autoa), statica(_statica) {
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, NSObject *obj) {
int autoa = __cself->autoa; // bound by copy
int *statica = __cself->statica; // bound by cop
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_93c220_mi_0,a);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_93c220_mi_1,obj);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_93c220_mi_2,autoa);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_93c220_mi_3,(*statica));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_93c220_mi_4,globle_a);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_93c220_mi_5,globlea_static_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;
auto int autob = 1;
auto int autoa = 1;
static int statica = 1;
void(*lc_block)(int , NSObject *) = ((void (*)(int, NSObject *))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, autoa, &statica));
((void (*)(__block_impl *, int, NSObject *))((__block_impl *)lc_block)->FuncPtr)((__block_impl *)lc_block, 10, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new")));
}
return 0;
}
void(*lc_block)(int , NSObject *) = ((void (*)(int, NSObject *))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, autoa, &statica));
这行代码
可以简化成这样 去掉一些强制烈性装换
lc_block = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, autoa, &statica)
__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, autoa, &statica)是__main_block_impl_0C++结构体的构造函数,可以理解成OC的初始化方法
根据上面的解释我们可以看出lc_block其实就是指向__main_block_impl_0结构体的指针到这里我们其实已经可以看到lc_block是怎么捕获外边的变量了__main_block_impl_0构造函数的 第一个参数(void)__main_block_func_0 就是block的实现函数指针, 第二个参数&__main_block_desc_0_DATA暂时先不做解释,第三,第四个参数autoa, &statica就是我们捕获外边的变量 ,说道这里我们至少明白了一个问题
(1)block是怎么捕获外边的变量的.
下面我们回答一下这两个问题 block是在__main_block_impl_0初始化的时候把需要的捕获的变量令作为参数传进__main_block_impl_0结构体的
下面我们再来分析一下block的调用
((void (*)(__block_impl *, int, NSObject *))((__block_impl *)lc_block)->FuncPtr)((__block_impl *)lc_block, 10, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new")))
去掉一些类型强制转换其实可以变成下面这样
lc_block->FuncPtr(lc_block, 10,[NSObject new]);
impl.FuncPtr = fp; FuncPtr就是我们在初始化结构体穿进去的第一个参数就是__main_block_func_0函数的指针,这里大家可能有个疑问按照正常的写法不应该是lc_block->impl.FuncPtr(lc_block, 10,[NSObject new]);这样才对嘛. 是的这样是没错,但是直接lc_block->FuncPtr加上类型强制转换也是没有问题的原因大概解释一下因为impl是结构体中第一个成员变量所以lc_block指针指向的其实是impl的地址.
lc_block->FuncPtr(lc_block, 10,[NSObject new]);就是调用了__main_block_func_0这个函数(再次强调一下这个函数是block的实现)这个函数的第一个参数就是block的指针.第二个第三个参数是我们自己传进去的.
看到这里我们可以基本可以回答两个问题.
(1)block捕获会捕获外边的什么变量
下面我们回答一下这两个问题block里面用到的变量block才会捕获但是block不会捕获 全局变量, static静态变量(可以🤔一下为啥block不会捕获全局变量,全局静态变量) 细心的朋友可能还发现了block捕获局部变量autoa是值传递,捕获局部静态变量statica是指针传递.这也就说明为啥block中可以修改静态变量不可以修改局部变量了(可以🤔一下为啥block不会捕获局部变量为啥是值传递,static变量为啥是指针传递)
下面我们分析block的类型 我们先分析在MAC情况下
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
void(^lcc_block)(void) = ^(void){
//捕获的外部变量
};
NSLog(@"%@",[lcc_block class]);
[lcc_block release];
}
return 0;
}
2020-01-04 21:29:07.556789+0800 block_1[64578:2231805] __NSGlobalBlock__
int globle_a = 1;
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
void(^lcc_block)(void) = ^(void){
//捕获的外部变量
NSLog(@"%d",globle_a);
};
NSLog(@"%@",[lcc_block class]);
[lcc_block release];
}
return 0;
}
2020-01-04 21:29:07.556789+0800 block_1[64578:2231805] __NSGlobalBlock__
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
static int statica = 1;
void(^lcc_block)(void) = ^(void){
//捕获的外部变量
NSLog(@"%d",statica);
};
NSLog(@"%@",[lcc_block class]);
[lcc_block release];
}
return 0;
}
2020-01-04 21:32:02.108197+0800 block_1[64603:2235019] __NSGlobalBlock__
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
static int statica = 1;
void(^lcc_block)(void) = ^(void){
//捕获的外部变量
NSLog(@"%d",autoa);
};
NSLog(@"%@",[lcc_block class]);
[lcc_block release];
}
return 0;
}
2020-01-04 21:33:35.248612+0800 block_1[64630:2237066] __NSStackBlock__
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
static int statica = 1;
void(^lcc_block)(void) = [^(void){
//捕获的外部变量
NSLog(@"%d",autoa);
} copy];
NSLog(@"%@",[lcc_block class]);
[lcc_block release];
}
return 0;
}
2020-01-04 21:34:23.187194+0800 block_1[64644:2238310] __NSMallocBlock__
我们总结一下结论
block类型 | 环境 |
---|---|
NSGlobalBlock | 没有访问auto变量 |
NSStackBlock | 访问了auto变量 |
NSMallocBlock | NSStackBlock调用了copy |
我们再分析ARC的情况
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
void(^lcc_block)(void) = ^(void){
//捕获的外部变量
};
NSLog(@"%@",[lcc_block class]);
}
return 0;
}
2020-01-04 21:42:00.940409+0800 block_1[64708:2245501] __NSGlobalBlock__
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
void(^lcc_block)(void) = ^(void){
//捕获的外部变量
NSLog(@"%d",globle_a);
};
NSLog(@"%@",[lcc_block class]);
}
return 0;
}
2020-01-04 21:42:56.328330+0800 block_1[64721:2246791] __NSGlobalBlock__
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
static int statica = 1;
void(^lcc_block)(void) = ^(void){
//捕获的外部变量
NSLog(@"%d",statica);
};
NSLog(@"%@",[lcc_block class]);
}
return 0;
}
2020-01-04 21:44:54.807266+0800 block_1[64745:2248664] __NSGlobalBlock__
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
static int statica = 1;
void(^lcc_block)(void) = ^(void){
//捕获的外部变量
NSLog(@"%d",autoa);
};
NSLog(@"%@",[lcc_block class]);
}
return 0;
}
2020-01-04 21:45:31.803956+0800 block_1[64756:2249690] __NSMallocBlock__
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
static int statica = 1;
void(^lcc_block)(void) = [^(void){
//捕获的外部变量
NSLog(@"%d",autoa);
} copy];
NSLog(@"%@",[lcc_block class]);
}
return 0;
}
2020-01-04 21:46:55.321199+0800 block_1[64767:2251090] __NSMallocBlock__
我们可以看出在MRC和在ARC的情况下不同的是block访问auto局部变量是NSMallocBlock 难道是我们以上总结的结论是错的吗? 答案是不是.
看下面代码
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
NSLog(@"%@",[^(void){NSLog(@"%d",autoa);} class]);
}
return 0;
}
2020-01-04 21:58:45.531138+0800 block_1[64814:2259295] __NSStackBlock__
在ARC的情况下这样的block还是NSStackBlock和我们上表格总结的访问了auto变量 是NSStackBlock并不矛盾
在ARC的情况下编译器在什么情况下会自动 将NSStackBlock调用了copy转成NSMallocBlock呢?
block作为函数返回值时
将block赋值给__strong指针时
block作为Cocoa API中方法名含有usingBlock的方法参数时
block作为GCD API的方法参数时
不知道大家有没有思考过这个问题编译器为什么要把栈block copy一下变成堆block, 这样的好处是啥?
带着疑问我们看下面的代码
//在MRC的情况下
void(^lc_blcok)(void);
void lc_test(){
auto int autoa = 1;
lc_blcok = ^(void){
NSLog(@"autoa = %d",autoa);
};
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
lc_test();
lc_blcok();
}
return 0;
}
2020-01-04 22:15:13.271055+0800 block_1[64958:2273028] __NSStackBlock__
2020-01-04 22:15:13.271751+0800 block_1[64958:2273028] autoa = -27
我们发现打印的autoa这个变量是个奇怪的值原因是lc_blcok是NSStackBlock虽然我们用一个全局指针lc_blcok指向这个block(也可以说是结构体 因为我们通过前面的代码分析出来block其实就是一个结构体的指针)但是这个结构体是在栈 上呢,在栈上的数据有个特点就是他的内存管理是系统控制的, lc_test函数以调用完毕内存就可呢释放了 所以block捕获进去的autoa也是垃圾内存了. 所以编译器会根据情况自动将栈上的block复制到堆上.
说道这里还能引申一个问题就是在block作为一个类的属性时到底是用strong 修饰还是用copy修饰呢
结论是
ARC的情况下 用strong和copy 都可以
MRC情况下建议用copy
原因相信聪明的你已经知道了
block捕获对象类型是否持有auto对象
下面我们根据 clang命令xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m再看看转成c++的代码 (我们考虑到有__weak的情况现在只讨论arc)
int main(int argc, const char * argv[]) {
@autoreleasepool {
LCTest1 *testObj = [[LCTest1 alloc]init];
testObj.age = 10;
LCTest1 *testObj2 = [[LCTest1 alloc]init];
testObj2.age = 10;
__weak LCTest1 *weakTestObj = testObj2;
void(^lc_block)(void) = ^(void){
NSLog(@"%@",testObj);
NSLog(@"%@",weakTestObj);
};
lc_block();
}
return 0;
}
下面是转成c++的关键代码
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;
LCTest1 *__strong testObj;
LCTest1 *__weak weakTestObj;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, LCTest1 *__strong _testObj, LCTest1 *__weak _weakTestObj, int flags=0) : testObj(_testObj), weakTestObj(_weakTestObj) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
LCTest1 *__strong testObj = __cself->testObj; // bound by copy
LCTest1 *__weak weakTestObj = __cself->weakTestObj; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_ba54bf_mi_0,testObj);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_ba54bf_mi_1,weakTestObj);
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->testObj, (void*)src->testObj, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_assign((void*)&dst->weakTestObj, (void*)src->weakTestObj, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->testObj, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_dispose((void*)src->weakTestObj, 3/*BLOCK_FIELD_IS_OBJECT*/);}
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;
LCTest1 *testObj = ((LCTest1 *(*)(id, SEL))(void *)objc_msgSend)((id)((LCTest1 *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LCTest1"), sel_registerName("alloc")), sel_registerName("init"));
((void (*)(id, SEL, int))(void *)objc_msgSend)((id)testObj, sel_registerName("setAge:"), 10);
LCTest1 *testObj2 = ((LCTest1 *(*)(id, SEL))(void *)objc_msgSend)((id)((LCTest1 *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LCTest1"), sel_registerName("alloc")), sel_registerName("init"));
((void (*)(id, SEL, int))(void *)objc_msgSend)((id)testObj2, sel_registerName("setAge:"), 10);
__attribute__((objc_ownership(weak))) LCTest1 *weakTestObj = testObj2;
void(*lc_block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, testObj, weakTestObj, 570425344));
((void (*)(__block_impl *))((__block_impl *)lc_block)->FuncPtr)((__block_impl *)lc_block);
}
return 0;
}
我们可以看出block在捕获外面auto变量和捕获基本类型的变量是有所不同的.
我们看__main_block_desc_0这个结构体里面多了两个函数指针变量
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
下面就是函数指针变量的实现 __main_block_copy_0函数会在block从栈被拷贝到堆上自动调用,
__main_block_dispose_0函数会在堆上的block被销毁的时候自动调用
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->testObj, (void*)src->testObj, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_assign((void*)&dst->weakTestObj, (void*)src->weakTestObj, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->testObj, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_dispose((void*)src->weakTestObj, 3/*BLOCK_FIELD_IS_OBJECT*/);}
我们看到 __main_block_impl_0结构体中捕获的 testObj的变量和weakTestObj变量 分别用__strong, __weak修饰
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
LCTest1 *__strong testObj;
LCTest1 *__weak weakTestObj;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, LCTest1 *__strong _testObj, LCTest1 *__weak _weakTestObj, int flags=0) : testObj(_testObj), weakTestObj(_weakTestObj) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
看到这里我们可能已经能够感觉到了block捕获外面的auto对象变量会通过__main_block_copy_0里面的_Block_object_assign函数和__main_block_dispose_0里面的_Block_object_dispose函数对auto对象变量做内存方面的管理 那我们是不是人为 只要是__strong修饰的对象block就会持有(是对象的引用计数+1)这个对象__weak修饰的对象就不会持有这个对象呢? 这个结论不太准确 我们举个例子
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
LCTest1 *testObj;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, LCTest1 *_testObj, int flags=0) : testObj(_testObj) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
可以看出 __main_block_impl_0中的捕获了testObj但是在61行testObj就被释放了所以block中并内有持有testObj
我们再看一个arc下的例子
int main(int argc, const char * argv[]) {
@autoreleasepool {
void(^lc_block)(void) = nil;
testObj1 = nil;
autoa++;
LCTest1 *testObj1 = [[LCTest1 alloc]init];
testObj1.age = 10;
^(void){
NSLog(@"%@",testObj1);
};
printf("testObj retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(testObj1)));
}
return 0;
}
testObj retain count = 1可以看出block也没有引用testObj
我们看下转换过之后的 c++关键代码 我们发现
虽然LCTest1 *__strong testObj1 是__strong但是testObj并没有被block强持有
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
LCTest1 *__strong testObj1;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, LCTest1 *__strong _testObj1, int flags=0) : testObj1(_testObj1) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
经过上面的分析我们 我们看到__strong修饰的变量,block并不一定会强引用这个__strong变量..
下面给出总结
如果block是在栈上,将不会对auto变量产生强引用
如果block被拷贝到堆上会调用block内部的copy函数
copy函数内部会调用_Block_object_assign函数
_Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用
下面我们再分析一下__block
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block LCTest1 *testObj1 = [[LCTest1 alloc]init];
__block int autoa = 1;
void(^lc_block)(void) = ^(void){
NSLog(@"%@",testObj1);
NSLog(@"%d",autoa);
};
lc_block();
printf("testObj retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(testObj1)));
}
return 0;
}
下面是转换成c++的代码
struct __Block_byref_testObj1_0 {
void *__isa;
__Block_byref_testObj1_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
LCTest1 *__strong testObj1;
};
struct __Block_byref_autoa_1 {
void *__isa;
__Block_byref_autoa_1 *__forwarding;
int __flags;
int __size;
int autoa;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_testObj1_0 *testObj1; // by ref
__Block_byref_autoa_1 *autoa; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_testObj1_0 *_testObj1, __Block_byref_autoa_1 *_autoa, int flags=0) : testObj1(_testObj1->__forwarding), autoa(_autoa->__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_testObj1_0 *testObj1 = __cself->testObj1; // bound by ref
__Block_byref_autoa_1 *autoa = __cself->autoa; // bound by ref
(testObj1->__forwarding->testObj1) = __null;
(autoa->__forwarding->autoa)++;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_d1c897_mi_0,(testObj1->__forwarding->testObj1));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_d1c897_mi_1,(autoa->__forwarding->autoa));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->testObj1, (void*)src->testObj1, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&dst->autoa, (void*)src->autoa, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->testObj1, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src->autoa, 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_testObj1_0 testObj1 = {
(void*)0,
(__Block_byref_testObj1_0 *)&testObj1,
33554432,
sizeof(__Block_byref_testObj1_0),
__Block_byref_id_object_copy_131,
__Block_byref_id_object_dispose_131,
((LCTest1 *(*)(id, SEL))(void *)objc_msgSend)((id)((LCTest1 *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LCTest1"), sel_registerName("alloc")), sel_registerName("init"))
};
__attribute__((__blocks__(byref))) __Block_byref_autoa_1 autoa = {
(void*)0,
(__Block_byref_autoa_1 *)&autoa,
0,
sizeof(__Block_byref_autoa_1),
1
};
void(*lc_block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_testObj1_0 *)&testObj1, (__Block_byref_autoa_1 *)&autoa, 570425344));
((void (*)(__block_impl *))((__block_impl *)lc_block)->FuncPtr)((__block_impl *)lc_block);
printf("testObj retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)((testObj1.__forwarding->testObj1))));
}
return 0;
}
可以看出__block修饰的变量在最终会被包装成一个结构体如果是__block修饰的是对象变量这个结构体中会有两个函数指针__Block_byref_id_object_copy,和__Block_byref_id_object_dispose分别对对象变量做内存方面的管理,结构体里面有个重要的指针__forwarding是指想自己的指针.
我们从block的实现中__main_block_func_0看到__Block_byref_testObj1_0 *testObj1 = __cself->testObj1; // bound by ref
__Block_byref_autoa_1 *autoa = __cself->autoa; // bound by ref
testObj1和autoa分别是包装成block的结构体的指针修改__block对象其实是通过
包装成block的指针 拿到 __forwarding(包装成block的指针)再拿到真正的变量然后修改
现在大家可能有一个疑问为啥不通过包装成block的指针直接拿到真正的变量
(testObj1->__forwarding->testObj1) = __null;
(autoa->__forwarding->autoa)++;
我们根据上面的图可以看出当block在栈上包装__block修饰的变量的结构体中__forwarding是指向自己,当block通过copy拷贝到堆上包装__block修饰的变量的结构体中__forwarding指向的就是堆上的, 猜测难道是我们再__main_block_func_0这个函数中使用的__Block_byref_testObj1_0 *testObj1和__Block_byref_autoa_1 *autoa 是栈上的? 如果是testObj1和autoa 是栈上的那么__forwarding指针就是上图说的意义了. 如果是堆上的__forwarding指针貌似没有存在的意义,因为完全可以通过下面这样修改堆上的结果,没必要通过__forwarding再转一下
(堆上的)
testObj1-> testObj1
(堆上的)
autoa-> autoa
我们可以用代码验证一下
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __Block_byref_obj2_0 {
void *__isa;
struct __Block_byref_obj2_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
NSObject *obj2;
};
struct __Block_byref_autoa_1 {
void *__isa;
struct __Block_byref_autoa_1 *__forwarding;
int __flags;
int __size;
int autoa;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
struct __Block_byref_obj2_0 *obj2; // by ref
struct __Block_byref_autoa_1 *autoa; // by ref
};
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*);
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 1;
NSObject *obj1 = [[NSObject alloc]init];
__block NSObject *obj2 = [[NSObject alloc]init];
__block int autoa = 10;
void(^lc_block)(void) = [^(void){
obj2 = nil;
autoa++;
} copy];
struct __main_block_impl_0 * Static_block = (__bridge struct __main_block_impl_0 *)((id)lc_block);
NSLog(@"lc_block class = %@",[lc_block class]);
lc_block();
}
return 0;
}
我们在lc_block();这一行代码打上断点
我们通过 po查看
2020-01-07 11:30:43.578077+0800 block_1[98305:3389230] lc_block class = __NSMallocBlock__
(lldb) po &a
0x00007ffeefbff54c
(lldb) po obj1
<NSObject: 0x1006758f0>
(lldb) po obj2
<NSObject: 0x100675930>
(lldb) po Static_block
<__NSMallocBlock__: 0x1006785b0>
signature: "v8@?0"
invoke : 0x100000da0 (/Users/lcc/Library/Developer/Xcode/DerivedData/block_1-heucsvushdyhgchdcamixyeeosnt/Build/Products/Debug/block_1`__main_block_invoke)
copy : 0x100000de0 (/Users/lcc/Library/Developer/Xcode/DerivedData/block_1-heucsvushdyhgchdcamixyeeosnt/Build/Products/Debug/block_1`__copy_helper_block_e8_32r40r)
dispose : 0x100000e40 (/Users/lcc/Library/Developer/Xcode/DerivedData/block_1-heucsvushdyhgchdcamixyeeosnt/Build/Products/Debug/block_1`__destroy_helper_block_e8_32r40r)
(lldb) po Static_block->obj2
0x00000001006785e0
(lldb) po Static_block->obj2->__forwarding
0x00000001006785e0
(lldb) po Static_block->obj2->__forwarding->obj2
<NSObject: 0x100675930>
(lldb) po &autoa
0x0000000100678628
(lldb) po Static_block->autoa
0x0000000100678610
(lldb) po Static_block->autoa->__forwarding
0x0000000100678610
(lldb) po &(Static_block->autoa->__forwarding->autoa)
0x0000000100678628
(lldb)
我们分析一下
&a 0x00007ffeefbff54c 这个肯定是栈地址
po obj1 <NSObject: 0x1006758f0> 这个肯定是堆地址
po Static_block 0x1006785b0 这个地址和obj1接近也可以看到到block的类型 却是是在堆
Static_block->obj2 0x00000001006785e0 这个地址也是堆
po &autoa 0x0000000100678628 我们看这个地址也是堆
可以看出 基本类型本来应该在栈上 经过__block修饰可以看出被分到了堆上
po Static_block->autoa 0x0000000100678610这个地址也是在堆上,可以看出我们直接用的就是堆上并不是栈上的 那么有必要 autoa-> __forwarding-> autoa 我想不通__forwarding存在的意义
==================================================
我们在分析一下栈上的block
#import <Foundation/Foundation.h>
#import "LCTest1.h"
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __Block_byref_obj2_0 {
void *__isa;
struct __Block_byref_obj2_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
NSObject *obj2;
};
struct __Block_byref_autoa_1 {
void *__isa;
struct __Block_byref_autoa_1 *__forwarding;
int __flags;
int __size;
int autoa;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
struct __Block_byref_obj2_0 *obj2; // by ref
struct __Block_byref_autoa_1 *autoa; // by ref
};
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*);
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 1;
NSObject *obj1 = [[NSObject alloc]init];
__block NSObject *obj2 = [[NSObject alloc]init];
__block int autoa = 10;
void(^lc_block)(void) = ^(void){
obj2 = nil;
autoa++;
};
struct __main_block_impl_0 * Static_block = (__bridge struct __main_block_impl_0 *)((id)lc_block);
NSLog(@"lc_block class = %@",[lc_block class]);
lc_block();
}
return 0;
}
2020-01-07 11:05:15.894243+0800 block_1[98239:3381416] lc_block class = __NSStackBlock__
(lldb) po &a
0x00007ffeefbff54c
(lldb) po obj1
<NSObject: 0x1006355b0>
(lldb) po Static_block
<__NSStackBlock__: 0x7ffeefbff4b8>
signature: "v8@?0"
invoke : 0x100000da0 (/Users/lcc/Library/Developer/Xcode/DerivedData/block_1-heucsvushdyhgchdcamixyeeosnt/Build/Products/Debug/block_1`__main_block_invoke)
copy : 0x100000de0 (/Users/lcc/Library/Developer/Xcode/DerivedData/block_1-heucsvushdyhgchdcamixyeeosnt/Build/Products/Debug/block_1`__copy_helper_block_e8_32r40r)
dispose : 0x100000e40 (/Users/lcc/Library/Developer/Xcode/DerivedData/block_1-heucsvushdyhgchdcamixyeeosnt/Build/Products/Debug/block_1`__destroy_helper_block_e8_32r40r)
(lldb) po Static_block->obj2
0x00007ffeefbff510
(lldb) po Static_block->obj2->__forwarding
0x00007ffeefbff510
(lldb) po Static_block->obj2->__forwarding->obj2
<NSObject: 0x100635920>
(lldb) po obj2
<NSObject: 0x100635920>
(lldb) po &autoa
0x00007ffeefbff508
(lldb) po Static_block->autoa
0x00007ffeefbff4f0
(lldb) po Static_block->autoa->__forwarding
0x00007ffeefbff4f0
(lldb) po &(Static_block->autoa->__forwarding->autoa)
0x00007ffeefbff508
po Static_block->obj2 0x00007ffeefbff510 是在栈上
po Static_block->obj2->__forwarding 0x00007ffeefbff510还是在栈上
po Static_block->autoa 0x00007ffeefbff4f0 是在栈上
po Static_block->autoa->__forwarding 0x00007ffeefbff4f0 还是在栈上
__forwarding也是没有存在的意义啊
经过我的分析我还是没有弄明白__forwarding存在的意义,哪位大佬了解拜托指教一二