block詳解<3>: block在內存區域中是如何分布的

如果大家還有映像的話,我們在前面講解結構體 _block_impl的時候,里面有一個成員叫isa,這個代表了block在內存區域中的分布。如果你看了一些關于block的文章,isa會有三種取值:

isa = &_NSConcreteStackBlock;
isa = &_NSConcreteMallocBlock;
isa = &_NSConcreteGlobalBlock;

但是clang出來的文件,里面都是第一種,說明這并不是block在內存中的真正分布。真正的分布,我們可以通過打印來確定。


屏幕快照 2016-09-22 2.35.54 PM.png
屏幕快照 2016-09-22 2.49.47 PM.png

第二張圖的打印結果是NSMallocBlock,同時我又試了其他的幾種情況,發現都是NSMallocBlock,說明在ARC環境下,棧上的block默認都會被拷貝到堆上,也就是說,在ARC環境下,block只有兩種類型:NSGlobalBlock 和 NSMallocBlock。那么到底有沒有特殊的情況呢?后來查查資料,發現還真有。

屏幕快照 2016-09-22 5.09.57 PM.png

執行上面的代碼的時候,直接crash了,從錯誤的提示可以看出,是某個東西的內存過早的釋放了。我們仔細觀察一下控制臺,發現數組的第一個元素是NSMallocBlock類型,這是被分配在堆上的block;數組的第二個元素有點兒問題:發現它的內存地址跟argv的格式比較像,而argv是函數的參數,內存是被分配在棧內存上的,所以第二個block也是被分配在棧內存上的,并沒有被拷貝到堆內存上。然后我們換一種寫法:

屏幕快照 2016-09-22 5.21.23 PM.png

發現可以正常運行,并且打印的結果也是我們想要的。

我們再看一下上面的數組的初始化函數

+ (instancetype)arrayWithObjects:(ObjectType)firstObj, ... NS_REQUIRES_NIL_TERMINATION;

這個函數是一個可變函數,只有第一個參數被顯示的申明為ObjectType類型,也就是id類型,其他的參數并沒有被顯示的申明為id類型。這也驗證了第一種情況下第一個block被分配在堆上,第二個block被分配在棧上。而我們的第二種寫法是,先申明一下block,在block到底是什么一文中,我們已經說了,block其實就是一個函數指針,也可以說它是一個id類型,所以在第二種寫法下,兩個block都被顯示的申明為id類型,所以都被分配在堆上,所以第二種情況沒有問題。

由此我們可以得出一個結論:block作為函數的參數時,一定要被顯示的申明為id類型,才會被分配在堆上。
參考文章

ARC 下向 NSArray 添加 Block 元素的一個小坑

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

推薦閱讀更多精彩內容