本地对象和block及__block修饰符
1、看其底层实现
//__block声明而多出来的结构体
struct __Block_byref_b_0 {
void *__isa;
__Block_byref_b_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
id b;
};
//block的描述
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的内部实现函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_b_0 *b = __cself->b; // bound by ref
id a = __cself->a; // bound by copy
......
}
//所要填充的结构体
struct __main_block_impl_0 {
......
id a;
__Block_byref_b_0 *b; // by ref
......
}
};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
id a = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
//定义一个对象a
__attribute__((__blocks__(byref))) __Block_byref_b_0 b = {(void*)0,(__Block_byref_b_0 *)&b, 33554432, sizeof(__Block_byref_b_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"))};
//定义一个对象b,且用__block修饰
......
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a, (__Block_byref_b_0 *)&b, 570425344));
//定义初始化block
......
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
//调用block
}
return 0;
}
2、实现对比
1、在ARC中
int main(int argc, const char * argv[]) {
@autoreleasepool {
id a = [[NSObject alloc]init];
__block id b = [[NSObject alloc]init];
NSLog(@"&a:%p ,&b:%p",&a,&b);
//&a:0x7ffeedfa0168 ,&b:0x7ffeedfa0160
void (^block)(void) = ^(void)
{
NSLog(@"&a:%p ,&b:%p",&a,&b);
};
NSLog(@"&a:%p ,&b:%p",&a,&b);
//&a:0x7ffeedfa0168 ,&b:0x604000054f68
block();
//&a:0x604000054f30 ,&b:0x604000054f68
//疑问 1:为什么本地对象a的地址在block中被修改了?
//疑问 2:为什么被__block修饰的本地对象b在block初始化的时候把自身的地址b更改了?
}
return 0;
}
2、在MRC中
int main(int argc, const char * argv[]) {
@autoreleasepool {
id a = [[NSObject alloc]init];
__block id b = [[NSObject alloc]init];
NSLog(@"&a:%p ,&b:%p",&a,&b);
//&a:0x7ffeebe9a168 ,&b:0x7ffeebe9a160
void (^block)(void) = ^(void)
{
NSLog(@"&a:%p ,&b:%p",&a,&b);
};
NSLog(@"&a:%p ,&b:%p",&a,&b);
//&a:0x7ffeebe9a168 ,&b:0x7ffeebe9a160
block();
//&a:0x7ffeebe9a110 ,&b:0x7ffeebe9a160
//疑问 1:为什么本地对象a的地址在block中被修改了?
//疑问 3:为什么被__block修饰的本地对象b的地址没有发生改变?
}
return 0;
}
3、在MRC中并将block作copy
int main(int argc, const char * argv[]) {
@autoreleasepool {
id a = [[NSObject alloc]init];
__block id b = [[NSObject alloc]init];
NSLog(@"&a:%p ,&b:%p",&a,&b);
//&a:0x7ffeedfa0168 ,&b:0x7ffeedfa0160
void (^block)(void) = [^(void)
{
NSLog(@"&a:%p ,&b:%p",&a,&b);
}copy]; //注意这里作了copy操作
NSLog(@"&a:%p ,&b:%p",&a,&b);
//&a:0x7ffeedfa0168 ,&b:0x604000054f68
block();
//&a:0x604000054f30 ,&b:0x604000054f68
//疑问 1:为什么本地对象a的地址在block中被修改了?
//疑问 4:为什么被__block修饰的本地对象b在block初始化的时候把自身的地址b更改了?
}
return 0;
}
疑问 1:为什么本地对象a的地址在block中被修改了?
- 因为在block的代码实现函数中新定义了一个a,所以在block内部输出的是新的a的地址
疑问 2:为什么被__block修饰的本地对象b在block初始化的时候把自身的地址b更改了?
- 因为将1中的block进行 = 操作了,所以在ARC中会导致调用objc_retainBlock->Block_copy->Block_copy_internal方法链。并导致 __NSStackBlock 类型的 block 转换为 NSMallocBlock 类型,所以对于__block修饰的本地对象b也copy了一份到堆上(通过__main_block_copy_0函数),而将原来的栈上的本地对象b释放了(通过__main_block_dispose_0()函数),得到的就是新的地址了。此过程发生在初始化block时候。而对于本地对象a呢会发现其引用计数加1
疑问 3:为什么被__block修饰的本地对象b的地址没有发生改变?
- 因为在2中的block明显是一个__NSStackBlock类型的,而且b传的是地址引用进block,通过访问此地址输出的b的地址自然都是一样。
疑问 4:为什么被__block修饰的本地对象b在block初始化的时候把自身的地址b更改了?
- 在3中,将__NSStackBlock进行copy后变成了一个__NSMallocBlock,多以对于__block修饰的本地对象b也copy了一份到堆上(通过__main_block_copy_0函数),而将原来的栈上的本地对象b释放了(通过__main_block_dispose_0()函数),得到的就是新的地址了。此过程发生在初始化block时候。而对于本地对象a呢会发现其引用计数加1
结论:
MRC中 :__block根本不会对指针所指向的对象执行copy操作,而只是把指针进行的复制。
ARC中 :对于声明为__block的外部对象,在block内部会进行retain,以至于在block环境内能安全的引用外部对象。