IOS基礎知識-block原理篇

面試題

block的原理是怎樣的?本質是什么?
__block的作用是什么?有什么使用注意點?
block的屬性修飾詞為什么是copy?使用block有哪些使用注意?
block在修改NSMutableArray,需不需要添加__block?

首先對block有一個基本的認識
block本質上也是一個oc對象,他內(nèi)部也有一個isa指針。block是封裝了函數(shù)調用以及函數(shù)調用環(huán)境的OC對象。
block中各個結構體之間的關系如下圖:


1434508-1ecebe9ad79c8c11.png

block底層數(shù)據(jù)結構如下圖:


1434508-b6f59468ae71b22b.png

為了保證block內(nèi)部能夠正常訪問外部的變量,block有一個變量捕獲機制:
局部變量都會被block捕獲,自動變量是值捕獲,靜態(tài)變量為地址捕獲。全局變量則不會被block捕獲;
block最終都是繼承自NSBlock類型,而NSBlock繼承于NSObjcet。那么block其中的isa指針其實是來自NSObject中的。這也更加印證了block的本質其實就是OC對象。
block調用copy會改變block類型嗎?
1434508-74811a740c972d8e.png

所以在平時開發(fā)過程中MRC環(huán)境下經(jīng)常需要使用copy來保存block,將棧上的block拷貝到堆中,即使棧上的block被銷毀,堆上的block也不會被銷毀,需要我們自己調用release操作來銷毀。而在ARC環(huán)境下回系統(tǒng)會自動copy,是block不會被銷毀。
什么情況下?ARC會對block做自動copy操作

block作為返回值時,比如使用“=”;
將block賦值給__strong指針時;
block作為Cocoa API中方法名含有usingBlock的方法參數(shù)時;
block作為GCD API的方法參數(shù)時;

總結

一旦block中捕獲的變量為對象類型,block結構體中的__main_block_desc_0會出兩個參數(shù)copy和dispose。因為訪問的是個對象,block希望擁有這個對象,就需要對對象進行引用,也就是進行內(nèi)存管理的操作。比如說對對象進行retarn操作,因此一旦block捕獲的變量是對象類型就會會自動生成copy和dispose來對內(nèi)部引用的對象進行內(nèi)存管理。
當block內(nèi)部訪問了對象類型的auto變量時,如果block是在棧上,block內(nèi)部不會對person產(chǎn)生強引用。不論block結構體內(nèi)部的變量是__strong修飾還是__weak修飾,都不會對變量產(chǎn)生強引用。
如果block被拷貝到堆上。copy函數(shù)會調用_Block_object_assign函數(shù),根據(jù)auto變量的修飾符(__strong,__weak,unsafe_unretained)做出相應的操作,形成強引用或者弱引用
如果block從堆中移除,dispose函數(shù)會調用_Block_object_dispose函數(shù),自動釋放引用的auto變量

  1. 下列代碼person在何時銷毀 ?
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    Person *person = [[Person alloc] init];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"%@",person);
    });
    NSLog(@"touchBegin----------End");
}
分析:在block執(zhí)行完畢后被釋放;在ARC環(huán)境中,block作為GCD API的方法參數(shù)時會自動進行copy操作,因此block在堆空間,并且使用強引用訪問person對象,因此block內(nèi)部copy函數(shù)會對person進行強引用。當block執(zhí)行完畢需要被銷毀時,調用dispose函數(shù)釋放對person對象的引用,person沒有強指針指向時才會被銷毀。

2、下列代碼person在何時銷毀 ?

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    Person *person = [[Person alloc] init];
    
    __weak Person *waekP = person;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"%@",waekP);
    });
    NSLog(@"touchBegin----------End");
}
分析:person先銷毀,才執(zhí)行block,打印為空;block中對waekP為__weak弱引用,因此block內(nèi)部copy函數(shù)會對person同樣進行弱引用,當大括號執(zhí)行完畢時,person對象沒有強指針引用就會被釋放。因此block塊執(zhí)行的時候打印null。

block中修改變量的值:
方式一:age使用static修飾。
前文提到過static修飾的age變量傳遞到block內(nèi)部的是指針,在__main_block_func_0函數(shù)內(nèi)部就可以拿到age變量的內(nèi)存地址,因此就可以在block內(nèi)部修改age的值。
方式二:__block
__block用于解決block內(nèi)部不能修改auto變量值的問題,__block不能修飾靜態(tài)變量(static) 和全局變量
__block為什么能修改變量的值?
__block將變量包裝成對象,然后在把age封裝在結構體里面,block內(nèi)部存儲的變量為結構體指針,也就可以通過指針找到內(nèi)存地址進而修改變量的值。
3、以下代碼是否可以正確執(zhí)行

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSMutableArray *array = [NSMutableArray array];
        Block block = ^{
            [array addObject: @"5"];
            [array addObject: @"5"];
            NSLog(@"%@",array);
        };
        block();
    }
    return 0;
}
分析:可以正確執(zhí)行,因為在block塊中僅僅是使用了array的內(nèi)存地址,往內(nèi)存地址中添加內(nèi)容,并沒有修改arry的內(nèi)存地址,因此array不需要使用__block修飾也可以正確編譯。因此當僅僅是使用局部變量的內(nèi)存地址,而不是修改的時候,盡量不要添加__block,通過上述分析我們知道一旦添加了__block修飾符,系統(tǒng)會自動創(chuàng)建相應的結構體,占用不必要的內(nèi)存空間。

解決循環(huán)引用問題 - ARC
使用__weak 和 __unsafe_unretained修飾符可以解決循環(huán)引用的問題;
__weak不會產(chǎn)生強引用,指向的對象銷毀時,會自動將指針置為nil。因此一般通過__weak來解決問題。

__unsafe_unretained不會產(chǎn)生前引用,不安全,指向的對象銷毀時,指針存儲的地址值不變。
解決循環(huán)引用問題 - MRC
使用__unsafe_unretained解決;

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

相關閱讀更多精彩內(nèi)容

  • Block的底層基本結構 通過clang命令查看編譯器是如何實現(xiàn)Block的,在終端輸入clang -rewrit...
    小涼介閱讀 20,469評論 22 96
  • 參考篇:iOS-Block淺談 前言:本文簡述Block本質,如有錯誤請留言指正。 第一部分:Block本質 Q:...
    夢蕊dream閱讀 61,948評論 41 323
  • 第一部分:Block本質 Q:什么是Block,Block的本質是什么? block本質上也是一個OC對象,它內(nèi)部...
    sheldon_龍閱讀 610評論 0 0
  • 腦子容易想歪了,負面情緒充斥全身
    kidII閱讀 225評論 0 0
  • 馬上就要回冬至了,今天,已是第23個節(jié)氣,不,是第24個了。 早晨睜眼的時候,陽光早已不再隨著黎明...
    天凈樹梓閱讀 205評論 1 2

友情鏈接更多精彩內(nèi)容