如果大家還有映像的話,我們在前面講解結(jié)構(gòu)體 _block_impl的時候,里面有一個成員叫isa,這個代表了block在內(nèi)存區(qū)域中的分布。如果你看了一些關(guān)于block的文章,isa會有三種取值:
isa = &_NSConcreteStackBlock;
isa = &_NSConcreteMallocBlock;
isa = &_NSConcreteGlobalBlock;
但是clang出來的文件,里面都是第一種,說明這并不是block在內(nèi)存中的真正分布。真正的分布,我們可以通過打印來確定。


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

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

發(fā)現(xiàn)可以正常運行,并且打印的結(jié)果也是我們想要的。
我們再看一下上面的數(shù)組的初始化函數(shù)
+ (instancetype)arrayWithObjects:(ObjectType)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
這個函數(shù)是一個可變函數(shù),只有第一個參數(shù)被顯示的申明為ObjectType類型,也就是id類型,其他的參數(shù)并沒有被顯示的申明為id類型。這也驗證了第一種情況下第一個block被分配在堆上,第二個block被分配在棧上。而我們的第二種寫法是,先申明一下block,在block到底是什么一文中,我們已經(jīng)說了,block其實就是一個函數(shù)指針,也可以說它是一個id類型,所以在第二種寫法下,兩個block都被顯示的申明為id類型,所以都被分配在堆上,所以第二種情況沒有問題。