ARC和MRC下的block的記憶體分布詳解

2021-08-01 12:55:27 字數 2491 閱讀 3646

block有三種型別,分別是

__nsglobalblock__:全域性block,程式被載入後被分配在程序資料段上(類似函式,位於text段),也就是常量,靜態建立的block。

__nsmallocblock__:在程序堆上分配的block,動態建立的block。

__nsstackblock__:程序棧上分配的block,動態建立的block。

在arc下我一直以為__nsstackblock__已經不會出現的了,事實上也並非如此,我們可以強行讓它出現,但實際開發中應該不會做這麼無聊的事。

先看看乙個demo。

——————————————————

- (void

)viewdidload ;

nslog

(@"%@"

, blocka);

intvalue = 10;

void

(^blockb)() = ^;

nslog

(@"%@"

, blockb);

void

(^ __weak

blockc)() = ^;

nslog

(@"%@"

, blockc);

}——————————————————

三個nslog列印的內容為:

<__nsglobalblock__: 0x105a730d0>

<__nsmallocblock__: 0x600000052c00>

<__nsstackblock__: 0x7fff5a18b7e0>

注意看它們的位址,__nsglobalblock__的位址明顯要短,因為它是在程序資料段上的。

blockc則是強行用__weak宣告讓其分配在棧上,這裡會看到乙個黃色的警告,大意就是指分配後就會被釋放。就是說viewdidload這個方法return後這個block就會被釋放。

動態分配和靜態分配的區分是在**?觀察一下就發現__nsglobalblock__型別是沒有捕獲區域性變數的,它只是列印一乙個字串。通過nsstring literal建立的字串是放在常量區的,也就是資料段上。全域性的block裡沒有引用任何堆或棧上的資料。另外如果將上面的例子中的int value = 10;改為const int value = 10;那麼blockb將變成__nsglobalblock__,這是因為const修飾下value裡的值會儲存在常量區即資料段上,也就是不違反原則,只要block literal裡沒有引用棧或堆上的資料,那麼這個block會自動變為__nsglobalblock__型別,這是編譯器的優化。

在屬性宣告上,我們一般會用copy修飾乙個block屬性。原因是什麼?

在mrr或mrc(兩個詞都是指同乙個玩意)中,block預設是在棧上建立的。如果我們將它賦值給乙個成員變數,如果成員變數沒有被copy修飾或在賦值的時候沒有進行copy,那麼在使用這個block成員變數的時候就會崩潰。因為棧上的出了作用域就被釋放掉了,copy操作可以將block從棧上copy到堆上

很早的時候mrc的block屬性都是在棧區的,copy之後就到堆區了

當前的arc的block屬性預設都在堆區,使用copy知識沿襲了歷史的習慣,使用strong也是沒有問題的

思考一下下面的**。

————————————————————————————————————

@property

(nonatomic

, weak

) void

(^block)();

- (void

)viewdidload ;

nslog

(@"%@"

, blockc);

_block = blockc;

}- (

ibaction

)action:(

id)sender

這**在mrc下是會崩潰的。但arc下就不會了,因為block預設就建立在堆上了。但是不是意味著arc不用寫copy來修飾block屬性呢?當然不是了,上面已經說了,我們是可以強行在arc上將乙個block建立在棧上的。

intvalue = 10;

void

(^ __weak

blockc)() = ^;

————————————————————————————————————

最後留乙個思考題,下面這種情況會不會崩毀?為什麼?

@property

(nonatomic

, weak

) void

(^block)();

- (void

)viewdidload ;

_block = blocka;

}- (

ibaction

)action:(

id)sender

上面的這個也是不會crash的,因為沒有引用臨時(內部或者外部變數),是個__nsglobalblock__型別的block,程式執行期間都不會釋放,所以根本就不用擔心野指標問題嘍!

ARC和MRC下Block的使用注意

1.是一段 塊,只在被呼叫的時候執行 類似於方法和函式 2.是一種資料型別 類似於 int nsstring 3.可以定義成臨時變數 4.可以當做引數傳遞 5.可以定義成屬性 6.是一種匿名函式 重要,只有函式體,沒有函式名 7.是乙個指向函式的指標 乙個指標物件,block的名字就是指標的位址 8...

block 的ARC和MRC中的區別

block屬性的宣告,首先需要用copy修飾符,因為只有copy後的block才會在堆中,棧中的block的生命週期是和棧繫結的,可以參考之前的文章 ios 非arc下返回block 另乙個需要注意的問題是關於執行緒安全,在宣告block屬性時需要確認 在呼叫block時另乙個執行緒有沒有可能去修改...

block在ARC和MRC中的區別

block在arc和mrc中的宣告引用有些區別.block可以儲存在棧中,也可以在堆中 預設儲存在棧中,不需要管理記憶體 儲存在堆中的block會對block進行retain操作 mrc block在堆中時,不想對block進行retain操作,前面加 block arc 前面加 weak或 uns...