block循環(huán)引用、三種分類担忧,block變量截獲芹缔,__block底層原理

一、block循環(huán)引用

場景:從viewController通過modal跳轉(zhuǎn)到ModelViewController瓶盛,然后點(diǎn)擊屏幕返回ViewController乖菱。

ViewController
ModelViewController

如上兩圖坡锡,當(dāng)從modelViewController點(diǎn)擊屏幕返回viewController后蓬网,會(huì)調(diào)用dealloc方法窒所,打印出銷毀。說明ModelViewController在方法dismissViewController后帆锋,就銷毀了吵取。原因是從ViewController跳轉(zhuǎn)到ModelViewController時(shí),前者的self.presentedViewController屬性指向了ModelViewController锯厢,強(qiáng)指針皮官,不會(huì)釋放。而當(dāng)dismiss后实辑,self.presentedViewController屬性消失捺氢,沒有強(qiáng)指針指向,所以ModelViewController銷毀了剪撬。

而如下圖摄乒,在block中打印一下self的值,dismiss后残黑,就不會(huì)打印銷毀了(不調(diào)用dealloc方法)馍佑,控制器沒有銷毀。問題就出在第25行代碼梨水,造成block循環(huán)利用:block會(huì)對代碼塊中的所有強(qiáng)指針變量都請引用一次拭荤。就是25行的self被_block屬性指向了,而self又是ModelViewController疫诽,self指向了ModelViewController舅世,控制器要銷毀就要self釋放,但是self被_block指向奇徒,無法釋放雏亚。控制器無法銷毀逼龟,也導(dǎo)致了_block屬性不能釋放评凝,就造成了循環(huán)引用,造成內(nèi)存泄漏腺律。

解決辦法如下奕短,將self包裝成一個(gè)弱指針,運(yùn)行后匀钧,ModelViewController成功銷毀翎碑。

但是當(dāng)我們對block塊中的代碼有要求的時(shí)候,讓block代碼延時(shí)執(zhí)行的話之斯,就會(huì)因?yàn)榭刂破飨蠕N毀日杈,拿不到相應(yīng)的數(shù)據(jù)。如圖

解決方法如圖,將weakSelf包裝成強(qiáng)指針莉擒,這樣一來strongSelf又指向了model控制器酿炸,控制器又不會(huì)釋放了,而strongSelf是_block屬性方法體內(nèi)的局部變量涨冀,只有延時(shí)執(zhí)行完了填硕,strongSelf才會(huì)釋放,model控制器才會(huì)銷毀釋放鹿鳖。所以這樣就會(huì)順利的將_block方法執(zhí)行完才會(huì)銷毀控制器扁眯。

二、三種block

GlobalBlock::沒有使用局部變量 or 使用靜態(tài)or全局變量

MallocBlock:使用了局部變量 or OC屬性翅帜,并且使用__strong修飾的(被強(qiáng)引用的)姻檀,在堆區(qū)

StackBlock:使用了局部變量,但是沒有被強(qiáng)引用的

三涝滴、block變量截獲

1.block的一個(gè)特性就是變量截獲绣版,注意是截獲,而不是獲取狭莱〗┩蓿看圖

我們在block1塊前創(chuàng)建int變量b,然后在調(diào)用block1();之前改變b的值腋妙,然而打印出來的b=0默怨。block會(huì)截獲block塊之前的變量,一旦截獲骤素,后續(xù)的外部更改是沒有用的匙睹。

2.經(jīng)過第一點(diǎn)的描述,我們知道了block截獲之后,外部的更改對截獲值沒有影響济竹。那么在block內(nèi)部進(jìn)行更改呢痕檬?會(huì)發(fā)生什么呢?

可以看到送浊,block中對變量b進(jìn)行更改梦谜,編譯直接報(bào)錯(cuò)。說明在block塊中是不能對外部的變量值進(jìn)行更改的袭景,注意是變量的值唁桩,如果是外部的引用(非值類型),會(huì)有一些不同耸棒,后面細(xì)說荒澡。

從代碼中看出,block內(nèi)部不能改變外部變量的值与殃,會(huì)報(bào)錯(cuò)单山。但是為什么不能修改呢碍现?上代碼分析

從代碼分析中我們可以發(fā)現(xiàn),block塊中的b的地址與block外部的b的地址打印是不一樣的米奸。其實(shí)block的變量截獲昼接,block進(jìn)行了一些操作,block在內(nèi)部創(chuàng)建了臨時(shí)的變量躏升,用于記錄外部的截獲到的變量值辩棒,本質(zhì)上block塊中的b和外部的b不是同一個(gè)變量,所以在block中無法改變外部的變量的值膨疏。

3.然后我們解決一下第2點(diǎn)中加黑的問題,前面舉的例子都是值類型的變量截獲以及操作钻弄,那么引用類型呢佃却?我們這里使用NSMutableArray進(jìn)行測試。上代碼

是不是發(fā)現(xiàn)了不同窘俺?引用類型的變量block內(nèi)外的地址是一致的饲帅。再測試一下能不能在block中進(jìn)行更改?下面兩個(gè)測試案例瘤泪,兩種更改方法灶泵,上代碼

可以看到第一個(gè)圖中,編譯報(bào)錯(cuò)对途,第二個(gè)圖中正常運(yùn)行赦邻。我們首先要區(qū)分開值類型變量和引用類型變量的區(qū)別。值類型僅僅是棧中的一個(gè)數(shù)值实檀,而引用變量的實(shí)質(zhì)是一個(gè)指針惶洲,指針指向的(指針的值)是我們在堆中創(chuàng)建的對象的地址,而這個(gè)指針本身的地址是在棧中的膳犹。 是不是發(fā)現(xiàn)了點(diǎn)什么恬吕?block中不能改變外部變量的值其實(shí)指的是棧上變量的值?

圖一中我們的做法须床,是重新創(chuàng)建了一個(gè)可變數(shù)組對象铐料,然后讓array更新指向,指向我們新創(chuàng)建的對象的地址豺旬。在這種做法中钠惩,我們實(shí)質(zhì)上是試圖更改array指針的值(因?yàn)槲覀円屗赶蚱渌麑ο螅羔樀闹稻褪撬赶驅(qū)ο蟮牡刂?哈垢,而array指針本身地址是在棧上的妻柒,我們對block外部的棧上的變量進(jìn)行更改,就會(huì)導(dǎo)致編譯錯(cuò)誤耘分,無法更改举塔。

而圖二中绑警,表面上是對array進(jìn)行操作,其實(shí)是我們對array指針?biāo)赶虻牡刂?也就是數(shù)組對象央渣,堆中)進(jìn)行操作计盒,并沒有更改array的指向(值),所以是可以正常運(yùn)行的芽丹。證實(shí)了我們前面的猜測北启。block中不能改變外部變量的值,實(shí)質(zhì)上是不能改變處在棧中的變量的值拔第。

通過上面的分析咕村,我們可以發(fā)現(xiàn)蚊俺,其實(shí)我們上面對array進(jìn)行%p的地址打印懈涛,其實(shí)打印出的是array所指向的堆中對象的地址的打印泳猬,并不是array變量本身的地址。所以我們對array指針本身地址進(jìn)行打印得封,上代碼埋心。

發(fā)現(xiàn)了什么,是不是和之前第2點(diǎn)中拷呆,打印 值類型變量b地址 的結(jié)果是一致的晨横?block仍然是在block內(nèi)部生成了一個(gè)臨時(shí)變量洋腮,保存了截獲的arrar指針的值(讓我們可以通過保存的值正常訪問堆中的地址),所以block里面和外面的&array的打印不一致手形。

四啥供、__block底層原理

1.那么在上面的分析中,我們知道了伙狐,block中不能更改棧上變量的值瞬欧。那么有沒有辦法進(jìn)行更改呢?當(dāng)然是有的艘虎,我們可以使用__block關(guān)鍵字修飾我們想更改的變量,就可以實(shí)現(xiàn)更改属划。

2.依舊是為什么恬叹?為什么使用__block修飾之后就可以更改了呢同眯??

簡要概括一下硅确,之前的分析明肮,可以知道block中不能對棧上的變量進(jìn)行更改。__block關(guān)鍵字的原理其實(shí)就是:進(jìn)行了一個(gè)將棧上變量晤愧,拷貝到堆中的一個(gè)操作。即將棧上的基本數(shù)據(jù)類型,轉(zhuǎn)變封裝為一個(gè)對象類型的操作烙丛。以此來實(shí)現(xiàn)可以對變量進(jìn)行更改。

總結(jié):__block關(guān)鍵字其實(shí)就是進(jìn)行了一個(gè)堆棧的轉(zhuǎn)換操作钠右,將棧中的數(shù)據(jù)拷貝到堆上忘蟹,然后便可以進(jìn)行更改飒房。(就類似于函數(shù)傳參,我們只有傳入引用類型媚值,在函數(shù)中才能對其進(jìn)行真正的操作更改,否則是無法更改的)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嚼松,一起剝皮案震驚了整個(gè)濱河市锰扶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌罕偎,老刑警劉巖京闰,帶你破解...
    沈念sama閱讀 221,888評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件甩苛,死亡現(xiàn)場離奇詭異器予,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)爱葵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門反浓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人辆雾,你說我怎么就攤上這事月劈。” “怎么了猜揪?”我有些...
    開封第一講書人閱讀 168,386評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵而姐,是天一觀的道長。 經(jīng)常有香客問我拴念,道長,這世上最難降的妖魔是什么风瘦? 我笑而不...
    開封第一講書人閱讀 59,726評(píng)論 1 297
  • 正文 為了忘掉前任缔俄,我火速辦了婚禮,結(jié)果婚禮上蟹略,老公的妹妹穿的比我還像新娘遏佣。我一直安慰自己,他們只是感情好状婶,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著草姻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪敞曹。 梳的紋絲不亂的頭發(fā)上综膀,一...
    開封第一講書人閱讀 52,337評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音橄登,去河邊找鬼讥此。 笑死,一個(gè)胖子當(dāng)著我的面吹牛萄喳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼闻蛀,長吁一口氣:“原來是場噩夢啊……” “哼您市!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起茵休,我...
    開封第一講書人閱讀 39,807評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤榕莺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后钉鸯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,349評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贸营,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了揣云。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冰啃。...
    茶點(diǎn)故事閱讀 40,567評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖翎迁,靈堂內(nèi)的尸體忽然破棺而出净薛,到底是詐尸還是另有隱情,我是刑警寧澤肃拜,帶...
    沈念sama閱讀 36,242評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站士聪,受9級(jí)特大地震影響猛蔽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜曼库,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評(píng)論 3 334
  • 文/蒙蒙 一毁枯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧种玛,春花似錦、人聲如沸娱节。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至讥电,卻和暖如春轧抗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背横媚。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留恢口,地道東北人穷躁。 一個(gè)月前我還...
    沈念sama閱讀 48,995評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像猿诸,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子梳虽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容