__block內存管理
上文提到當block中捕獲對象類型的變量時,block中的__main_block_desc_0結構體內部會自動添加copy和dispose函數(shù)對捕獲的變量進行內存管理。
那么同樣的當block內部捕獲__block修飾的對象類型的變量時,__Block_byref_person_0結構體內部也會自動添加__Block_byref_id_object_copy和__Block_byref_id_object_dispose對被__block包裝成結構體的對象進行內存管理。
當block內存在棧上時,并不會對__block變量產(chǎn)生內存管理。當blcok被copy到堆上時 會調用block內部的copy函數(shù),copy函數(shù)內部會調用_Block_object_assign函數(shù),_Block_object_assign函數(shù)會對__block變量形成強引用(相當于retain)
首先通過一張圖看一下block復制到堆上時內存變化

當block被copy到堆上時,block內部引用的__block變量也會被復制到堆上,并且持有變量,如果block復制到堆上的同時,__block變量已經(jīng)存在堆上了,則不會復制。
當block從堆中移除的話,就會調用dispose函數(shù),也就是__main_block_dispose_0函數(shù),__main_block_dispose_0函數(shù)內部會調用_Block_object_dispose函數(shù),會自動釋放引用的__block變量。

block內部決定什么時候將變量復制到堆中,什么時候對變量做引用計數(shù)的操作。
__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++代碼查看不同變量之間的區(qū)別
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)則根據(jù)他們本身被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兩個函數(shù),用來對對象類型的變量進行內存管理的操作。而結構體對對象的引用類型,則取決于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函數(shù)中會根據(jù)變量是強弱指針及有沒有被__block修飾做出不同的處理,強指針在block內部產(chǎn)生強引用,弱指針在block內部產(chǎn)生弱引用。被__block修飾的變量最后的參數(shù)傳入的是8,沒有被__block修飾的變量最后的參數(shù)傳入的是3。
當block從堆中移除時通過dispose函數(shù)來釋放他們。
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*/);
}