__block内存管理
上文提到当block中捕获对象类型的变量时,block中的__main_block_desc_0
结构体内部会自动添加copy
和dispose
函数对捕获的变量进行内存管理。
那么同样的当block内部捕获__block
修饰的对象类型的变量时,__Block_byref_person_0
结构体内部也会自动添加__Block_byref_id_object_copy
和__Block_byref_id_object_dispose
对被__block
包装成结构体的对象进行内存管理。
当block
内存在栈上时,并不会对__block
变量产生内存管理。当blcok
被copy
到堆上时 会调用block
内部的copy
函数,copy
函数内部会调用_Block_object_assign
函数,_Block_object_assign
函数会对__block
变量形成强引用(相当于retain)
首先通过一张图看一下block复制到堆上时内存变化
当block
被copy
到堆上时,block
内部引用的__block
变量也会被复制到堆上,并且持有变量,如果block
复制到堆上的同时,__block
变量已经存在堆上了,则不会复制。
当block从堆中移除的话,就会调用dispose函数,也就是__main_block_dispose_0
函数,__main_block_dispose_0
函数内部会调用_Block_object_dispose
函数,会自动释放引用的__block变量。
block内部决定什么时候将变量复制到堆中,什么时候对变量做引用计数的操作。
__block
修饰的变量在block结构体中一直都是强引用,而其他类型的是由传入的对象指针类型决定。
一段代码更深入的观察一下。
typedef void (^Block)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
int number = 20;
__block int age = 10;
NSObject *object = [[NSObject alloc] init];
__weak NSObject *weakObj = object;
Person *p = [[Person alloc] init];
__block Person *person = p;
__block __weak Person *weakPerson = p;
Block block = ^ {
NSLog(@"%d",number); // 局部变量
NSLog(@"%d",age); // __block修饰的局部变量
NSLog(@"%p",object); // 对象类型的局部变量
NSLog(@"%p",weakObj); // __weak修饰的对象类型的局部变量
NSLog(@"%p",person); // __block修饰的对象类型的局部变量
NSLog(@"%p",weakPerson); // __block,__weak修饰的对象类型的局部变量
};
block();
}
return 0;
}
将上述代码转化为c++代码查看不同变量之间的区别
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int number;
NSObject *__strong object;
NSObject *__weak weakObj;
__Block_byref_age_0 *age; // by ref
__Block_byref_person_1 *person; // by ref
__Block_byref_weakPerson_2 *weakPerson; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _number, NSObject *__strong _object, NSObject *__weak _weakObj, __Block_byref_age_0 *_age, __Block_byref_person_1 *_person, __Block_byref_weakPerson_2 *_weakPerson, int flags=0) : number(_number), object(_object), weakObj(_weakObj), age(_age->__forwarding), person(_person->__forwarding), weakPerson(_weakPerson->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
上述__main_block_impl_0
结构体中看出,没有使用__block
修饰的变量(object 和 weadObj)则根据他们本身被block捕获的指针类型对他们进行强引用或弱引用,而一旦使用__block
修饰的变量,__main_block_impl_0
结构体内一律使用强指针引用生成的结构体。
接着我们来看__block
修饰的变量生成的结构体有什么不同
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
struct __Block_byref_person_1 {
void *__isa;
__Block_byref_person_1 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
Person *__strong person;
};
struct __Block_byref_weakPerson_2 {
void *__isa;
__Block_byref_weakPerson_2 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
Person *__weak weakPerson;
};
如上面分析的那样,__block
修饰对象类型的变量生成的结构体内部多了__Block_byref_id_object_copy
和__Block_byref_id_object_dispose
两个函数,用来对对象类型的变量进行内存管理的操作。而结构体对对象的引用类型,则取决于block捕获的对象类型的变量。weakPerson
是弱指针,所以__Block_byref_weakPerson_2
对weakPerson
就是弱引用,person
是强指针,所以__Block_byref_person_1
对person就是强引用。
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*/);
_Block_object_assign((void*)&dst->object, (void*)src->object, 3/*BLOCK_FIELD_IS_OBJECT*/);
_Block_object_assign((void*)&dst->weakObj, (void*)src->weakObj, 3/*BLOCK_FIELD_IS_OBJECT*/);
_Block_object_assign((void*)&dst->person, (void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_assign((void*)&dst->weakPerson, (void*)src->weakPerson, 8/*BLOCK_FIELD_IS_BYREF*/);
}
__main_block_copy_0
函数中会根据变量是强弱指针及有没有被__block
修饰做出不同的处理,强指针在block内部产生强引用,弱指针在block内部产生弱引用。被__block
修饰的变量最后的参数传入的是8,没有被__block
修饰的变量最后的参数传入的是3。
当block从堆中移除时通过dispose函数来释放他们。
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_dispose((void*)src->object, 3/*BLOCK_FIELD_IS_OBJECT*/);
_Block_object_dispose((void*)src->weakObj, 3/*BLOCK_FIELD_IS_OBJECT*/);
_Block_object_dispose((void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_dispose((void*)src->weakPerson, 8/*BLOCK_FIELD_IS_BYREF*/);
}