在介紹今天的博客之前,你應(yīng)該了解,block的三大類型,block的變量捕獲機(jī)制,哪些情況下是NSMallcBlock,這些內(nèi)容我前面的博客都有介紹的非常清楚,相信你看了會(huì)有很多收獲.
通過(guò)這篇博客的學(xué)習(xí),你將會(huì)了解到:
__block它當(dāng)修飾符的時(shí)候,它的底層到底是怎么實(shí)現(xiàn)的.
首先看下面的代碼:
這里為什么去修改變量的值會(huì)報(bào)錯(cuò),大家知道嗎?這時(shí)候我們?nèi)グ旬?dāng)前的main.m文件轉(zhuǎn)成c++文件,因?yàn)槲覀冎纎c的底層就是c/c++,然后是匯編語(yǔ)言,然后是機(jī)器語(yǔ)言,可以通過(guò)下面的命令在終端執(zhí)行:(注意age=20注釋,不然轉(zhuǎn)不了)
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
我們想在一個(gè)函數(shù)里面修改另一個(gè)函數(shù)的age值,那是不是非常不可能啊,而上面的func_0里面訪問(wèn)的age是之前編譯器里面存儲(chǔ)的age,跟那個(gè)變量沒(méi)有任何關(guān)系,要改也只能改block里面的age,所以我們上面那么寫就是有問(wèn)題.
如果我們?cè)趨?shù)前面加一個(gè)static參數(shù)就不會(huì)報(bào)錯(cuò)了!請(qǐng)看下圖
這里就不說(shuō)為什么了,因?yàn)槲覀冎纒tatic它底層實(shí)現(xiàn)是傳的是*age,也就是存的是地址值,所以我們只要拿到這個(gè)地址,可以隨便修改這個(gè)變量
重點(diǎn)來(lái)了
請(qǐng)看下面的代碼,我用__block來(lái)當(dāng)作修飾符如下:
為什么這里用__block就是沒(méi)有問(wèn)題的呢?我們還是轉(zhuǎn)成c++代碼看一下底層的實(shí)現(xiàn):
這里很清楚,只要我用__block修飾變量,就會(huì)吧變量包裝成對(duì)象,上面能看到是結(jié)構(gòu)體,這個(gè)非常清楚,至于為啥__forwarding訪問(wèn)后面我會(huì)再說(shuō)
下面我來(lái)解釋一下*__forwarding這個(gè)是什么東西.請(qǐng)看下面,是我把多余的刪了的(大家可以對(duì)照原來(lái)的刪一下多余的代碼,便于我們觀看):
這個(gè)賦值非常明顯__forwarding賦值的就是&age,sizeof就是當(dāng)前的結(jié)構(gòu)體有多大,就是賦值size,而且最下面那個(gè)框框,他自己定義的age會(huì)傳給第二個(gè)參數(shù)&age,賦值給*__forwarding,所以__Block_byref_age_0它的地址就是*__forwarding,這個(gè)也非常清楚
再看一下block內(nèi)部也會(huì)把&age這個(gè)值傳給age這個(gè)值(這個(gè)就不截圖了,直接可以看得到?GDblock block = ((void(*)())&__main_block_impl_0((void*)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age,570425344));就是這段代碼找__main_block_impl_0實(shí)現(xiàn)就能看出賦值)
請(qǐng)看下面的代碼:
這種會(huì)不會(huì)報(bào)錯(cuò)?答案是不會(huì),因?yàn)槲也](méi)有修改它,我只是在用它.
相信到此大家都會(huì)清楚:block存儲(chǔ)著這個(gè)結(jié)構(gòu)體的內(nèi)存地址的值,修改的時(shí)候,會(huì)通過(guò)內(nèi)存地址找到這個(gè)結(jié)構(gòu)體,然后修改這個(gè)值
到此為止:__block修飾符修飾的變量 為什么block能修改,相信已經(jīng)很清楚
總結(jié):
1.__block可以用于解決block內(nèi)部無(wú)法修改auto變量值的問(wèn)題
2.__block不能修飾全局變量、靜態(tài)變量(static)(大家自己嘗試,會(huì)報(bào)錯(cuò))
3.編譯器會(huì)將__block包裝成一個(gè)對(duì)象
接下來(lái)可能有些人有點(diǎn)疑惑的就是如果我?NSLog(@"%p",&age);這個(gè)age的地址到底是哪個(gè)地址?如下
如下:
下面我們就來(lái)證明一下這個(gè)問(wèn)題,這個(gè)思路我之前的博客有,就是把底層實(shí)現(xiàn)的結(jié)構(gòu)體,之前拿過(guò)來(lái)用
從這里我們可以看出,它并不是block里面存儲(chǔ)的age的地址值,這2個(gè)值并不是一樣,我可以告訴大家,它訪問(wèn)的就是age里面那個(gè)age=10的那個(gè)age地址,你看下圖,我來(lái)證明
首先你要知道:大age結(jié)構(gòu)體的地址值,就是它第一個(gè)元素的地址值,所以0x0000000101cb92a0就是age結(jié)構(gòu)體里面的_isa的地址.這個(gè)要清楚
這里很清楚的證明了,確實(shí)是age結(jié)構(gòu)體里面的那個(gè)age的值