__block內存管理

__block內存管理

上文提到當block中捕獲對象類型的變量時,block中的__main_block_desc_0結構體內部會自動添加copydispose函數(shù)對捕獲的變量進行內存管理。

那么同樣的當block內部捕獲__block修飾的對象類型的變量時,__Block_byref_person_0結構體內部也會自動添加__Block_byref_id_object_copy__Block_byref_id_object_dispose對被__block包裝成結構體的對象進行內存管理。

block內存在棧上時,并不會對__block變量產(chǎn)生內存管理。當blcokcopy到堆上時 會調用block內部的copy函數(shù),copy函數(shù)內部會調用_Block_object_assign函數(shù),_Block_object_assign函數(shù)會對__block變量形成強引用(相當于retain)

首先通過一張圖看一下block復制到堆上時內存變化

blockcopy到堆上時,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_2weakPerson就是弱引用,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*/);

}

2020面試刷題與技術儲備專區(qū)

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容