首先,在了解block三大類型之前,我們需要了解一個(gè)知識(shí):
(溫馨提醒:如果我的之前博客你沒(méi)有看,有些概念你不清楚的話,你可能很難理解,如果前面你都看了,這篇博客你看就像切菜一樣簡(jiǎn)單!)
程序的內(nèi)存分配
一個(gè)由C/C++編譯的程序占用的內(nèi)存分為以下幾個(gè)部分
棧(stack):由編譯器自動(dòng)分配釋放 ,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。
堆(heap): 一般由程序員分配釋放, 若程序員不釋放,程序結(jié)束時(shí)可能由OS回收 。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事,分配方式類似于鏈表。
全局區(qū)(靜態(tài)區(qū)static):全局變量和靜態(tài)變量的存儲(chǔ)是放在一塊的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域, 未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域,程序結(jié)束后由系統(tǒng)釋放。
文字常量區(qū):常量字符串放在這里, 程序結(jié)束后由系統(tǒng)釋放
程序代碼區(qū):存放函數(shù)體的二進(jìn)制代碼。
相信大家對(duì)上面的這些內(nèi)存分配的知識(shí)點(diǎn)都已經(jīng)非常清楚,那我們就看一下
block的類型
block是有3種類型,通過(guò)調(diào)用class的方法或者isa指針查看具體的類型,最終都是繼承NSBlock類型
接下來(lái)我們就看看,block就是一個(gè)oc對(duì)象,那我們就可以按照oc的,直接調(diào)用class就知道它的類型了

這里可以看出我前面說(shuō)的,block就是oc對(duì)象,而這個(gè)NSGlobalBlock是繼承NSBlock的,其他的種類都是繼承NSBlock,等說(shuō)完了,大家可以用這個(gè)方法試試其它2種block的的父類.

上面就是block的總共的三種類型:NSGlobalBlock、NSStackBlock、NSMallcBlock,
,這是運(yùn)行時(shí)輸出的結(jié)果,其實(shí)編譯的結(jié)果可能有點(diǎn)不一樣,那我們就把這份文件轉(zhuǎn)成c++文件,這個(gè)我之前的博客都提了很多次,這次我就不細(xì)說(shuō)了(xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp)

其實(shí)你會(huì)發(fā)現(xiàn),編譯以后生成的都是NSConcreteStackBlock類型,首先我們肯定以運(yùn)行時(shí)為準(zhǔn),因?yàn)榫幾g過(guò)程還有個(gè)運(yùn)行,運(yùn)行就會(huì)用runtime動(dòng)態(tài)修改你的東西;還有其實(shí)用clang轉(zhuǎn)成c++的代碼,有時(shí)候并不一定就是最終的代碼,其實(shí)差別是不大,其實(shí)在llvm大概6.0以前都是一樣的,現(xiàn)在會(huì)生成一個(gè)中間文件,還有就是程序運(yùn)行中也是比較準(zhǔn)確,如果有同學(xué)查看源碼感覺(jué)不一樣,不要很驚訝,這個(gè)很正常,可以參考,差別不大.
我們繼續(xù)看

而GlobalBlock是在數(shù)據(jù)區(qū)域,MallocBlock是在堆區(qū),StackBlock是在棧區(qū),我這里不說(shuō)堆、棧、數(shù)據(jù)區(qū)域的區(qū)別了,大家這個(gè)知識(shí)點(diǎn)如果不懂可以去查閱一下它們的區(qū)別,不然后面的很難理解
什么樣的類型的是GlobalBlock?什么樣的類型是MallocBlock?什么樣的類型是StackBlock?
先看我的總結(jié),我們?cè)偌?xì)細(xì)看

auto變量大家清楚吧?auto變量就是它所在的區(qū)域執(zhí)行完畢,自動(dòng)銷毀的變量.如果不知道請(qǐng)看我上一篇博客,介紹的非常清晰.接下來(lái)我們就一個(gè)一個(gè)看每種類型的block
GlobalBlock類型
請(qǐng)看下面的代碼

訪問(wèn)static的局部變量,全局變量a,都不是auto變量,所以上面3種情況都是GlobalBlock,因?yàn)镚lobalBlock我們實(shí)際中用的非常少,所以這個(gè)也沒(méi)有過(guò)多的需要闡述的
StackBlock類型(重點(diǎn))
按照我們表格上面說(shuō)的訪問(wèn)了auto變量的block就是StackBlock,那我看一下下面的代碼:

應(yīng)該有同學(xué)注意,結(jié)果和我們上面的結(jié)論不一樣,其實(shí)是因?yàn)檫@環(huán)境是ARC,ARC下其實(shí)幫你做了很多事(這個(gè)為什么會(huì)這樣,這個(gè)內(nèi)容還是有點(diǎn)多,后面的博客,我會(huì)細(xì)說(shuō),你先不管這個(gè)為什么是這樣),所以我們改成MRC才能看到它的本質(zhì)(直接把Objective-C Automatic Reference Counting 設(shè)置為NO即可),我們?cè)倏聪旅?/p>

這就是StackBlock類型,下面我們?cè)偕钊肟匆幌逻@個(gè)StackBlock一個(gè)特殊情況,如下圖

注意到?jīng)]有,這個(gè)age的值變得很奇怪.這是因?yàn)?test()函數(shù)的作用域執(zhí)行結(jié)束以后,它的作用域中的棧上面的變量就會(huì)銷毀,比如里面的block就是StackBlock,所有test()執(zhí)行完畢以后,block已經(jīng)被釋放了,導(dǎo)致訪問(wèn)內(nèi)部就會(huì)出現(xiàn)混亂的情況.整個(gè)block內(nèi)存被釋放了里面的所有都會(huì)出現(xiàn)混亂
那上面的怎么解決這種問(wèn)題呢???
答案很明顯,我們把block棧內(nèi)存變成堆內(nèi)存就行了哇!怎么變成堆block,是不是直接棧調(diào)用copy就能是堆block了,那我們?cè)僭囋?

是不是就很容易解決了!可能有同學(xué)還有疑問(wèn),如果把GlobalBlock也來(lái)調(diào)用copy會(huì)是怎么樣呢?這個(gè)大家可以自己驗(yàn)證,答案還是:GlobalBlock,如果把MallocBlock也來(lái)調(diào)用copy會(huì)是怎么樣呢?這個(gè)大家可以自己驗(yàn)證,答案還是:引用計(jì)數(shù)加1
還有如果我們block訪問(wèn)一個(gè)對(duì)象,一個(gè)對(duì)象也是auto變量,所以也是一樣的,這些我就不驗(yàn)證了,如有疑問(wèn)可以評(píng)論區(qū)討論.
MallocBlock類型
這個(gè)上面已經(jīng)說(shuō)的很清楚了
相信還有同學(xué)有疑問(wèn),剛剛我們是MRC下測(cè)試的,至于ARC為什么會(huì)是另一個(gè)結(jié)果,我后面的博客會(huì)說(shuō)到!