從源碼、ARC、MRC帶你理解block的三大類型

首先,在了解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,那我看一下下面的代碼:

ARC下運(yùn)行的結(jié)果

應(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>

MRC下運(yùn)行的結(jié)果

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

MRC下運(yùn)行的結(jié)果

注意到?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ō)到!

接下來(lái)博客我會(huì)介紹堆MallocBlock的一些具體情況和使用,來(lái)繼續(xù)探討block

如果覺(jué)得我寫(xiě)得對(duì)您有所幫助,請(qǐng)關(guān)注我,我會(huì)持續(xù)更新??

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容