__block 可以用来解决block内部无法修改auto变量值的问题
__blcok不能修饰全局变量、静态变量(static)
编译器会将__block变量包装成一个对象
代码示例
============================== 原始代码 ==============================
@interface TBPerson : NSObject
@property (nonatomic, strong) NSString *name;
@end
@implementation TBPerson
@end
@interface ViewController ()
@end
@implementation ViewController
typedef void (^TBBlock) (void);
- (void)viewDidLoad {
[super viewDidLoad];
// __block修饰的基础数据类型
__block int age = 10;
// __block修饰的对象类型
__block TBPerson *person = [[TBPerson alloc] init];
person.name = @"111";
// 对象类型的auto变量
TBPerson *person1 = [[TBPerson alloc] init];
person1.name = @"000";
TBBlock block = ^{
age = 20;
person.name = @"222";
NSLog(@"age is %d; person -- name is %@; person1 -- name is %@",age,person.name,person1.name);
};
block();
}
============================== 使用clang转换为C++猜测底层实现 ==============================
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
// __block int age = 10;
__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};
// __block TBPerson *person = [[TBPerson alloc] init];
__attribute__((__blocks__(byref))) __Block_byref_person_1 person = {(void*)0,(__Block_byref_person_1 *)&person, 33554432, sizeof(__Block_byref_person_1), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((TBPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((TBPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("TBPerson"), sel_registerName("alloc")), sel_registerName("init"))};
// person.name = @"111";
((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)(person.__forwarding->person), sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_lx_q2n5w01149j15zhtyt93nzpr0000gn_T_ViewController_a6f2e3_mi_0);
// TBPerson *person1 = [[TBPerson alloc] init];
TBPerson *person1 = ((TBPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((TBPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("TBPerson"), sel_registerName("alloc")), sel_registerName("init"));
// person1.name = @"000";
((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)person1, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_lx_q2n5w01149j15zhtyt93nzpr0000gn_T_ViewController_a6f2e3_mi_1);
// block
TBBlock block = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, person1, (__Block_byref_age_0 *)&age, (__Block_byref_person_1 *)&person, 570425344));
// 调用block方法
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
// block内部要执行的函数
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {
// __block变量包装成一个对象__Block_byref_age_0
__Block_byref_age_0 *age = __cself->age; // bound by ref
__Block_byref_person_1 *person = __cself->person; // bound by ref
TBPerson *person1 = __cself->person1; // bound by copy
/*
为什么要使用age->__forwarding->age、person->__forwarding->person方式来访问
当变量从栈上拷贝到堆上的时候,可以保证不管是从栈上进行的访问,还是从堆上进行的访问,最后都一定是访问的堆上的变量
*/
(age->__forwarding->age) = 20;
((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)(person->__forwarding->person), sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_lx_q2n5w01149j15zhtyt93nzpr0000gn_T_ViewController_a6f2e3_mi_2);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_lx_q2n5w01149j15zhtyt93nzpr0000gn_T_ViewController_a6f2e3_mi_3,(age->__forwarding->age),((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)(person->__forwarding->person), sel_registerName("name")),((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)person1, sel_registerName("name")));
}
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
TBPerson *person1; // block捕获的外部变量
__Block_byref_age_0 *age; // 指向结构体对象 struct __Block_byref_age_0 (就是个强引用,不存在弱引用)
__Block_byref_person_1 *person; // 指向结构体对象 struct __Block_byref_person_1 (就是个强引用,不存在弱引用)
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, TBPerson *_person1, __Block_byref_age_0 *_age, __Block_byref_person_1 *_person, int flags=0) : person1(_person1), age(_age->__forwarding), person(_person->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
static struct __ViewController__viewDidLoad_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __ViewController__viewDidLoad_block_impl_0*, struct __ViewController__viewDidLoad_block_impl_0*);
void (*dispose)(struct __ViewController__viewDidLoad_block_impl_0*);
} __ViewController__viewDidLoad_block_desc_0_DATA = {
0,
sizeof(struct __ViewController__viewDidLoad_block_impl_0),
__ViewController__viewDidLoad_block_copy_0,
__ViewController__viewDidLoad_block_dispose_0
};
/*
当block在栈上的时候,并不会对__block变量产生强引用
当block被copy到堆上时
会调用block内部的copy函数
copy函数内部会调用_Block_object_assign函数
_Block_object_assign函数会对 __block变量形成强引用(retain)
*/
static void __ViewController__viewDidLoad_block_copy_0(struct __ViewController__viewDidLoad_block_impl_0*dst, struct __ViewController__viewDidLoad_block_impl_0*src) {
_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_assign((void*)&dst->person, (void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_assign((void*)&dst->person1, (void*)src->person1, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
/*
当block从堆上移除时
会调用block内部的dispose函数
dispose函数内部会调用_Block_object_dispose函数
_Block_object_dispose函数会自动释放引用的 __block变量(release)
*/
static void __ViewController__viewDidLoad_block_dispose_0(struct __ViewController__viewDidLoad_block_impl_0*src) {
_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_dispose((void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_dispose((void*)src->person1, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
// 基本数据类型转C++(__block int age 包装后的对象)
struct __Block_byref_age_0 {
void *__isa; // 指向结构体的类型
__Block_byref_age_0 *__forwarding; // 指向自己
int __flags; // 暂时未用到
int __size; // 结构体的大小
int age; // 变量的值
};
// 对象类型转C++(__block TBPerson *person 包装后的对象)
struct __Block_byref_person_1 {
void *__isa; // 指向结构体的类型
__Block_byref_person_1 *__forwarding; // 指向自己
int __flags; // 暂时未用到
int __size; // 结构体的大小
void (*__Block_byref_id_object_copy)(void*, void*); // 对象实例的内存管理:copy函数的地址
void (*__Block_byref_id_object_dispose)(void*); // 对象实例的内存管理:dispose函数的地址
/*
在ARC下:
可以是强引用(__block TBPerson *person),也可以是弱引用(__block __weak TBPerson *person),主要看外部修饰词
在MRC下:
不会对TBPerson *person进行retain(也就是说一直是弱引用)
*/
TBPerson *person; // 指向对象实例
};
/*
当__block被copy到堆上时
会调用__block内部的copy函数
copy函数内部会调用_Block_object_assign函数
_Block_object_assign函数会根据指向对象的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用
(注意:这里仅限于ARC时才会retain,在MRC环境时不会retain)
*/
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
// (char*)dst + 40 就是TBPerson *person(8+8+4+4+8+8)
_Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
/*
当__block从堆上移除时
会调用__block内部的dispose函数
dispose函数内部会调用_Block_object_dispose函数
_Block_object_dispose函数会自动释放指向的对象(release)
*/
static void __Block_byref_id_object_dispose_131(void *src) {
_Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}
============================== 总结 ==============================
一、对象类型的auto变量、__block变量 的内存管理
当block在栈上时,对它们都不会产生强引用
当block拷贝到推上时,都会通过copy函数来处理它们
__block变量: _Block_object_assign((void*)&dst->person, (void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);
对象类型的auto变量: _Block_object_assign((void*)&dst->person1, (void*)src->person1, 3/*BLOCK_FIELD_IS_OBJECT*/);
当block从堆上移除时,都会通过dispose函数来释放它们
__block变量: _Block_object_dispose((void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);
对象类型的auto变量: _Block_object_dispose((void*)src->person1, 3/*BLOCK_FIELD_IS_OBJECT*/);
注意:
__block变量 : BLOCK_FIELD_IS_BYREF
对象类型auto变量 : BLOCK_FIELD_IS_OBJECT
二、针对的是__block包装的结构体的内存管理(如:__Block_byref_age_0 *age; __Block_byref_person_1 *person;)
当block在栈上的时候,并不会对__block变量产生强引用
当block被copy到堆上时
会调用block内部的copy函数
copy函数内部会调用_Block_object_assign函数
_Block_object_assign函数会对 __block变量形成强引用(retain)
当block从堆上移除时
会调用block内部的dispose函数
dispose函数内部会调用_Block_object_dispose函数
_Block_object_dispose函数会自动释放引用的 __block变量(release
三、针对的是__block包装的结构体内部指向的对象(__block修饰的对象类型)的内存管理
当__block变量在栈上的时候,并不会对指向的对象产生强引用
当__block被copy到堆上时
会调用__block内部的copy函数
copy函数内部会调用_Block_object_assign函数
_Block_object_assign函数会根据指向对象的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用
(注意:这里仅限于ARC时才会retain,在MRC环境时不会retain)
当__block从堆上移除时
会调用__block内部的dispose函数
dispose函数内部会调用_Block_object_dispose函数
_Block_object_dispose函数会自动释放指向的对象(release)
注意:
1、block内部需要修改auto变量的值时才需要加__block
2、如果是使用 则不用加__block 如数组的添加、删除元素等操作
3、如果block外面访问__block修饰的变量时,访问的是 包装后的对象里面的变量 如:
struct __Block_byref_age_0 --> int age (访问的是age)
struct __Block_byref_person_1 --> TBPerson *person (访问的是person)
__block变量的copy示意图(截取自课程内容)
__block变量的dispose示意图(截取自课程内容)
__block变量的__forwarding指针示意图(截取自课程内容)
PS 此文为学习 李明杰 老师的 iOS底层原理课程 所写笔记