Block-棧,堆,全局塊

棧塊、堆塊、全局塊 (Block詳解)

對於Block之前只是在用,對於棧,堆這塊沒有細入研究,今天抽空把”Effectiv Objective-c 2.0”這本書看了一下,下面是一些概念及實例。

分幾類

  • 棧Block
  • 堆Block
  • 全局Block
    block的存儲形態有三種:_NSConcretStackBlock(棧)、_NSConcretGlobalBlock(全局)、_NSConcretMallocBlock(堆)

要點一:當block在函數內部,且定義的時候就使用了函數內部的變量,那麼這個 block是存儲在棧上的。

要點二:當block定義在函數體外面,或者定義在函數體內部且當時函數執行的時候,block體中並沒有需要使用函數內部的局部變量時,也就是block在函數執行的時候只是靜靜地待在一邊定義了一下而不使用函數體的內容,那麼block將會被編譯器存儲爲全局block。

要點三:全局block存儲在堆中,對全局block使用copy操作會返回原函數指針;而對棧中的block使用copy操作,會產生兩個不同的block地址,也就是兩個匿名函數的入口地址。

棧Block

直接聲明使用的block是分配在棧內,爲棧block,只會在定義它的範圍內有效。

注意:下面這種寫法MRC環境下就有問題。

-(void)doSomething{
    void (^block)();
    if(/* some condition */){
        block = ^{
            NSLog(@"Block A");
        };
    }else{
        block = ^{
            NSLog(@"Block B");
        };
    }
    block();
}

定義在if及else語句中的兩個block都分配在棧內存中,編譯器會給每個塊分配好棧內存,等離開了相應的範圍之後,編譯器有可能把分配給塊的內存覆寫掉,這兩個塊只能保證在對應的if或else語句範圍內有效。能正常編譯,但運行起來時而正確,時而錯誤,若編譯器未覆寫待執行的塊,則程序運行正常,若覆寫,則和序崩潰。

對於此類問題,可給塊對象發送copy消息,拷貝。這樣就可以把block從棧複製到堆,拷貝到堆中後, block就成了帶引用計數的對象了,後續的複製只會遞增對象的引用計數,如果不再使用這個塊,就需要釋放,ARC下系統統會自動釋放,當引用計數降爲0後,與其它對象一致,爲系統回收。

而分配在棧上的block,無須明確釋放,因爲棧內存本來就會自動回收,上面那段代碼之所以有風險,就是來源於此。

介與此,MRC環境下,上面那段代碼如此修改就沒有問題了。

-(void)doSomething{
    void (^block)();
    if(/* some condition */){
        block = [^{
            NSLog(@"Block A");
        } copy];
    }else{
        block = [^{
            NSLog(@"Block B");
        } copy];
    }
    block();
}

除了”棧塊”和”堆塊”之外,還有一類塊叫做”全局塊”,這種塊不會捕捉任何狀態,運行時也無須有狀態來參與,塊所使用的整個內存區域,在編譯期已經完全確定了,因此,全局塊可以聲明在全局內存裏,而不需要在每次用至的時候於棧中創建。別外全局塊的拷貝操作是個空操作,因爲全局塊決不可能爲系統所回收,這種塊實際相當於單例。下面就是個全局塊:

    void (^block)()=^{
        NSLog(@"This is a block");
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章