iOS-底層原理(10)-block-__block屬性詳解

__block修飾符

  • __block可以用于解決block內部無法修改auto變量值的問題
  • __block不能修飾全局變量、靜態變量(static)
  • 編譯器會將__block變量包裝成一個對象

結構圖如下

結構圖.png
__block的本質

代碼佐證

// 定義一個block
typedef void (^CSBlock) (void);

// block內部實現
struct __Block_byref_age_0 {
    void *__isa;
    struct __Block_byref_age_0 *__forwarding;
    int __flags;
    int __size;
    int age;
};

struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
    void (*copy)(void);
    void (*dispose)(void);
};

struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    struct __Block_byref_age_0 *age;
};

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        __block int age = 10;
        
        CSBlock block = ^{
            age = 20;
            NSLog(@"age is %d", age);
        };
        
        struct __main_block_impl_0 *blockImpl = (__bridge struct __main_block_impl_0 *)block;
        
        NSLog(@"%p", &age);
    }
    return 0;
}
image.png

可知外界訪問age,實際上在block內部是訪問結構體__Block_byref_age_0中的age變量

三種變量在block中的表現形式
image.png
image.png

通過查看源碼可知,不同屬性修飾的變量,在block中表現形式不同

__block的內存管理
  • 1.當block在棧上時,并不會對__block變量產生強引用

  • 2.當block被copy到堆時

    • 會調用block內部的copy函數
    • copy函數內部會調用_Block_object_assign函數
    • _Block_object_assign函數會對__block變量形成強引用(retain)
image.png
image.png
  • 3.當block從堆中移除時
    • 會調用block內部的dispose函數
    • dispose函數內部會調用_Block_object_dispose函數
    • _Block_object_dispose函數會自動釋放引用的__block變量(release)
image.png

對象類型的auto變量、__block變量 異同

  • 1.當block在棧上時,對它們都不會產生強引用

  • 2.當block拷貝到堆上時,都會通過copy函數來處理它們

__block變量(假設變量名叫做a)
_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);

對象類型的auto變量(假設變量名叫做p)
_Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);

  • 3.當block從堆上移除時,都會通過dispose函數來釋放它們

__block變量(假設變量名叫做a)
_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);

對象類型的auto變量(假設變量名叫做p)
_Block_object_dispose((void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);

image.png
__block的__forwarding指針
被__block修飾的對象類型
  • 1.當__block變量在棧上時,不會對指向的對象產生強引用

  • 2.當__block變量被copy到堆時

    • 會調用__block變量內部的copy函數
    • copy函數內部會調用_Block_object_assign函數
    • _Block_object_assign函數會根據所指向對象的修飾符(__strong、__weak、__unsafe_unretained)做出相應的操作,形成強引用(retain)或者弱引用(注意:這里僅限于ARC時會retain,MRC時不會retain)
  • 3.如果__block變量從堆上移除

    • 會調用__block變量內部的dispose函數
    • dispose函數內部會調用_Block_object_dispose函數
    • _Block_object_dispose函數會自動釋放指向的對象(release)

代碼實現如下

  • ARC環境下,有__weak修飾
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 演示被__block修飾的變量類型
        // 1.有__weak修飾
        CSBlock block;
        {
            Person *person = [[Person alloc] init];
            __block __weak Person *weakPerson = person;
            block = ^ {
                NSLog(@"%p",weakPerson);
            };
        }
        block();
    }
    return 0;
}

運行結果

image.png
  • ARC環境下,沒有__weak修飾
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 演示被__block修飾的變量類型
        // 1.有__weak修飾
        CSBlock block;
        {
            Person *person = [[Person alloc] init];
            __block Person *weakPerson = person;
            block = ^ {
                NSLog(@"%p",weakPerson);
            };
        }
        block();
    }
    return 0;
}

運行結果


image.png
  • MRC環境下,沒有__weak修飾
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 3.MRC環境下
        Person *person = [[Person alloc] init];
        
        CSBlock block = [^{
            NSLog(@"%p", person);
        } copy];
        
        [person release];
        
        block();
        
        [block release];
    }
    return 0;
}
image.png

本文參考借鑒MJ的教程視頻,非常感謝.


項目演示代碼
iOS-block-__block的本質


更多block相關文章

iOS-block底層原理詳解-本質,類型,copy屬性

iOS-copy底層原理之auto變量

iOS-block底層原理之循環引用詳解

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容