iOS中的閉包(二)

一、內(nèi)存分區(qū)

內(nèi)存區(qū)域

棧區(qū)(stack):由系統(tǒng)自動(dòng)分配,一般存放函數(shù)參數(shù)值、局部變量的值等。由編譯器自動(dòng)創(chuàng)建與釋放。其操作方式類(lèi)似于數(shù)據(jù)結(jié)構(gòu)中的棧,即后進(jìn)先出、先進(jìn)后出的原則。

堆區(qū)(heap):一般由程序員申請(qǐng)并指明大小,最終也由程序員釋放。如果程序員不釋放,程序結(jié)束時(shí)可能會(huì)由OS回收。對(duì)于堆區(qū)的管理是采用鏈表式管理的,操作系統(tǒng)有一個(gè)記錄空閑內(nèi)存地址的鏈表,當(dāng)接收到程序分配內(nèi)存的申請(qǐng)時(shí),操作系統(tǒng)就會(huì)遍歷該鏈表,遍歷到一個(gè)記錄的內(nèi)存地址大于申請(qǐng)內(nèi)存的鏈表節(jié)點(diǎn),并將該節(jié)點(diǎn)從該鏈表中刪除,然后將該節(jié)點(diǎn)記錄的內(nèi)存地址分配給程序。

全局區(qū)/靜態(tài)區(qū):顧名思義,全局變量和靜態(tài)變量存儲(chǔ)在這個(gè)區(qū)域。只不過(guò)初始化的全局變量和靜態(tài)變量存儲(chǔ)在一塊,未初始化的全局變量和靜態(tài)變量存儲(chǔ)在一塊。程序結(jié)束后由系統(tǒng)釋放。

文字常量區(qū):這個(gè)區(qū)域主要存儲(chǔ)字符串常量。程序結(jié)束后由系統(tǒng)釋放。

程序代碼區(qū):這個(gè)區(qū)域主要存放函數(shù)體的二進(jìn)制代碼。

二、閉包

閉包(Closure)簡(jiǎn)單理解就是一個(gè)函數(shù),加上這個(gè)函數(shù)相關(guān)的引用環(huán)境。block實(shí)際是Objc對(duì)閉包的實(shí)現(xiàn)。

三、Block中變量的復(fù)制與修改

  • 對(duì)于block外的變量引用,block默認(rèn)是將其復(fù)制到其數(shù)據(jù)結(jié)構(gòu)中來(lái)實(shí)現(xiàn)訪問(wèn)的
  • 通過(guò)block進(jìn)行引用的變量是const的。也就是說(shuō)不能在block中直接修改這些變量
  • 用__block關(guān)鍵字來(lái)聲明變量,這樣就可以在block中修改變量了,對(duì)于用__block修飾的外部變量引用,block是復(fù)制其引用的地址來(lái)實(shí)現(xiàn)訪問(wèn)的

四、Block的類(lèi)型

block主要有三種類(lèi)型,三種類(lèi)型也說(shuō)明了其在內(nèi)存中存儲(chǔ)的方式:

__NSGlobalBlock__:全局block并不是指全局聲明的block,而是指不會(huì)訪問(wèn)任何外部變量,不會(huì)涉及到任何拷貝的block,比如一個(gè)不執(zhí)行任何代碼的block

__NSStackBlock__:保存在棧中的block,當(dāng)函數(shù)返回時(shí)被銷(xiāo)毀

__NSMallocBlock__:保存在堆中的block,當(dāng)引用計(jì)數(shù)為0時(shí)被銷(xiāo)毀。該類(lèi)型的block都是由__NSStackBlock__類(lèi)型的block從棧中復(fù)制到堆中形成的。

五、Block從??截惖蕉焉系臅r(shí)機(jī)

當(dāng)我們創(chuàng)建一個(gè)block的時(shí)候,block是分配在棧上的(__NSGlobalBlock__例外),類(lèi)型為_(kāi)_NSStackBlock__,但是當(dāng)下面的一些情形時(shí)會(huì)把棧上的block拷貝到堆上變成__NSMallocBlock__

  • 調(diào)用Block的copy實(shí)例方法時(shí)
  • Block作為函數(shù)返回值返回時(shí)
  • 將Block賦值給附有__strong修飾符id類(lèi)型的類(lèi)或者Block類(lèi)型成員變量時(shí)
  • 在方法名中含有usingBlock的Cocoa框架方法或Grand Central Dispatch 的API中傳遞Block時(shí)

六、屬性Block用copy還是strong?

對(duì)于當(dāng)前的ARC對(duì)于block的處理,當(dāng)把一個(gè)block賦值給一個(gè)strong類(lèi)型的屬性時(shí),會(huì)自動(dòng)把block拷貝到堆上,并產(chǎn)生對(duì)該block的一個(gè)強(qiáng)引用。同樣地,copy類(lèi)型的block會(huì)在被賦值的時(shí)候進(jìn)行一次copy操作,如果原來(lái)的block是在棧上,那么也會(huì)被復(fù)制到堆上。所以對(duì)于block類(lèi)型的屬性,用strong和copy并沒(méi)有本質(zhì)上的區(qū)別,推薦使用copy,可以顯式地指定拷貝的動(dòng)作。當(dāng)然,如果block原本是堆block,那么內(nèi)部并沒(méi)有copy,只是進(jìn)行了一次retain操作;如果block原本是全局的block,那么也沒(méi)有copy,只是一次簡(jiǎn)單的賦值引用的操作

- (void)testBlock {
    
    //1.沒(méi)有引用外部變量,block1為_(kāi)_NSGlobalBlock__
    void(^block1)() = ^{
        NSLog(@"1");
    };
    NSLog(@"%@",block1);
    
    //2.引用了外部變量,默認(rèn)生成在棧上,該block為_(kāi)_NSStackBlock__類(lèi)型
    int num = 0;
    NSLog(@"%@",^{
        NSLog(@"%d",num);
    });
    
    //3.生成在棧上的block賦值給強(qiáng)引用,拷貝到堆上,block2為_(kāi)_NSMallocBlock__
    void(^block2)() = ^{
        NSLog(@"%d",num);
    };
    NSLog(@"%@",block2);
    
    
    //4.全局block可以執(zhí)行
    NSArray *globalBlocks = [self getGlobalBlocks];
    for (void(^block)() in globalBlocks) {
        NSLog(@"%@",block);
        block();
        
        // 對(duì)于全局的block進(jìn)行copy操作,并不會(huì)生成新的block,使用的其實(shí)是相同的block
        void(^copyBlock)() = [block copy];
        NSLog(@"%d", copyBlock == block);
    }
    
    
    //5.堆上的block可以在外部執(zhí)行
    NSArray *mallocBlocks = [self getMallocBlocks];
    for (void(^block)() in mallocBlocks) {
        NSLog(@"%@",block);
        block();
        
        // 和全局block一樣,堆上的block進(jìn)行copy操作也不會(huì)生成新的block
        void(^copyBlock)() = [block copy];
        NSLog(@"%d", copyBlock == block);
    }
    
    // 6.棧上的block在外部使用(野指針錯(cuò)誤)
    NSArray *stackBlocks = [self getStackBlocks];
    for (void(^block)() in stackBlocks) {
        NSLog(@"%@",block);
        block();
    }
}

- (NSArray *)getGlobalBlocks {
    // 沒(méi)有對(duì)外部變量進(jìn)行應(yīng)用的block為全局block,類(lèi)型是__NSGlobalBlock__
    // 全局block不需要copy或者retain處理
    return [NSArray arrayWithObjects:^{NSLog(@"1");},
            ^{NSLog(@"2");},
            ^{NSLog(@"3");},
            nil];
}

- (NSArray *)getMallocBlocks {
    // 棧上的block賦給一個(gè)強(qiáng)引用,會(huì)自動(dòng)從棧上copy到堆上
    // 當(dāng)返回一個(gè)block數(shù)組的時(shí)候注意要先把block拷貝到堆上,然后再加入的數(shù)組
    int num = 1;
    void(^block1)(void) = ^{NSLog(@"%i",num);};
    void(^block2)(void) = ^{NSLog(@"%i",num);};
    void(^block3)(void) = ^{NSLog(@"%i",num);};
    return [NSArray arrayWithObjects:block1,block2,block3,
            nil];
}


- (NSArray *)getStackBlocks {
    
    /* 
    這里有個(gè)奇怪的問(wèn)題,網(wǎng)上沒(méi)有發(fā)現(xiàn)對(duì)應(yīng)的解釋。下面數(shù)組中的第一個(gè)為_(kāi)_NSMallocBlock__,后面的卻為_(kāi)_NSStackBlock__,
    在方法外執(zhí)行數(shù)組中的block在執(zhí)行到第二個(gè)block的時(shí)候會(huì)發(fā)生崩潰
    
    個(gè)人的理解是這樣的:
    - 首先需要明確,棧block只能在作用域中執(zhí)行,出了作用域就會(huì)被銷(xiāo)毀,而堆block的銷(xiāo)毀取決于自身的引用計(jì)數(shù),可以在作用域外調(diào)用
    - block默認(rèn)創(chuàng)建在棧上,但數(shù)組對(duì)棧上的對(duì)象強(qiáng)引用無(wú)效,因?yàn)闂I系腷lock的銷(xiāo)毀是作用域決定的,強(qiáng)引用只能對(duì)堆上的對(duì)象起作用,所以數(shù)組的block在方法返回以后就被銷(xiāo)毀了。
    - 在數(shù)組的內(nèi)部應(yīng)該保存了隊(duì)首的位置,在添加元素的時(shí)候,這個(gè)隊(duì)首的引用應(yīng)該是指向第一個(gè)元素,這樣就產(chǎn)生了一次賦值操作,所以把第一個(gè)
    棧block拷貝到了堆上轉(zhuǎn)換成堆block,并進(jìn)行了一次retain操作
    */
    int num = 1;
    NSArray *arr =  [NSArray arrayWithObjects:^{NSLog(@"%i",num);},
            ^{NSLog(@"%i",num);},
            ^{NSLog(@"%i",num);},
            nil];
    NSLog(@"%@",arr);
    
    /*
    注:
    // block1為_(kāi)_NSMallocBlock__,后面的block2和block3還是__NSStackBlock__,
    // block4,block5為_(kāi)_NSMallocBlock__
    void(^block1)() = arr[0];
    void(^block2)() = arr[1];
    void(^block3)() = arr[2];
    
    // block2賦值給強(qiáng)引用block4,把棧上的block拷貝到堆上
    // block2進(jìn)行copy操作,把棧上的block拷貝到堆上
    void(^block4)() = block2;
    void(^block5)() = [block2 copy];
    NSLog(@"\n%@\n%@\n%@\n%@\n%@",block1,block2,block3,block4,block5);
    */
    
    return arr;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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