如果大家還有映像的話,我們?cè)谇懊嬷v解結(jié)構(gòu)體 _block_impl的時(shí)候甸赃,里面有一個(gè)成員叫isa,這個(gè)代表了block在內(nèi)存區(qū)域中的分布柿汛。如果你看了一些關(guān)于block的文章,isa會(huì)有三種取值:
isa = &_NSConcreteStackBlock;
isa = &_NSConcreteMallocBlock;
isa = &_NSConcreteGlobalBlock;
但是clang出來的文件埠对,里面都是第一種苛茂,說明這并不是block在內(nèi)存中的真正分布已烤。真正的分布,我們可以通過打印來確定妓羊。
第二張圖的打印結(jié)果是NSMallocBlock胯究,同時(shí)我又試了其他的幾種情況,發(fā)現(xiàn)都是NSMallocBlock躁绸,說明在ARC環(huán)境下裕循,棧上的block默認(rèn)都會(huì)被拷貝到堆上,也就是說净刮,在ARC環(huán)境下剥哑,block只有兩種類型:NSGlobalBlock 和 NSMallocBlock。那么到底有沒有特殊的情況呢淹父?后來查查資料株婴,發(fā)現(xiàn)還真有。
執(zhí)行上面的代碼的時(shí)候暑认,直接crash了困介,從錯(cuò)誤的提示可以看出,是某個(gè)東西的內(nèi)存過早的釋放了蘸际。我們仔細(xì)觀察一下控制臺(tái)座哩,發(fā)現(xiàn)數(shù)組的第一個(gè)元素是NSMallocBlock類型,這是被分配在堆上的block粮彤;數(shù)組的第二個(gè)元素有點(diǎn)兒?jiǎn)栴}:發(fā)現(xiàn)它的內(nèi)存地址跟argv的格式比較像根穷,而argv是函數(shù)的參數(shù),內(nèi)存是被分配在棧內(nèi)存上的导坟,所以第二個(gè)block也是被分配在棧內(nèi)存上的屿良,并沒有被拷貝到堆內(nèi)存上。然后我們換一種寫法:
發(fā)現(xiàn)可以正常運(yùn)行惫周,并且打印的結(jié)果也是我們想要的管引。
我們?cè)倏匆幌律厦娴臄?shù)組的初始化函數(shù)
+ (instancetype)arrayWithObjects:(ObjectType)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
這個(gè)函數(shù)是一個(gè)可變函數(shù),只有第一個(gè)參數(shù)被顯示的申明為ObjectType類型闯两,也就是id類型褥伴,其他的參數(shù)并沒有被顯示的申明為id類型。這也驗(yàn)證了第一種情況下第一個(gè)block被分配在堆上漾狼,第二個(gè)block被分配在棧上重慢。而我們的第二種寫法是,先申明一下block逊躁,在block到底是什么一文中似踱,我們已經(jīng)說了,block其實(shí)就是一個(gè)函數(shù)指針,也可以說它是一個(gè)id類型核芽,所以在第二種寫法下囚戚,兩個(gè)block都被顯示的申明為id類型,所以都被分配在堆上轧简,所以第二種情況沒有問題驰坊。